diff --git a/.builds/freebsd.yml b/.builds/freebsd.yml deleted file mode 100644 index 15d09dda0c09..000000000000 --- a/.builds/freebsd.yml +++ /dev/null @@ -1,33 +0,0 @@ -## DO NO EDIT DIRECTLY! auto-generated by `nim r tools/ci_generate.nim` - -# see https://man.sr.ht/builds.sr.ht/compatibility.md#freebsd -image: freebsd/latest -packages: -- databases/sqlite3 -- devel/boehm-gc-threaded -- devel/pcre -- devel/sdl20 -- devel/sfml -- www/node -- devel/gmake - - -sources: -- https://github.com/nim-lang/Nim -environment: - NIM_TESTAMENT_BATCH: "0_1" - CC: /usr/bin/clang -tasks: -- setup: | - set -e - cd Nim - . ci/funs.sh && nimBuildCsourcesIfNeeded - echo 'export PATH=$HOME/Nim/bin:$PATH' >> $HOME/.buildenv -- test: | - set -e - cd Nim - . ci/funs.sh && nimInternalBuildKochAndRunCI -triggers: -- action: email - condition: failure - to: Andreas Rumpf diff --git a/.builds/openbsd_0.yml b/.builds/openbsd_0.yml deleted file mode 100644 index c3b2fd43e89f..000000000000 --- a/.builds/openbsd_0.yml +++ /dev/null @@ -1,33 +0,0 @@ -## DO NO EDIT DIRECTLY! auto-generated by `nim r tools/ci_generate.nim` - -image: openbsd/latest -packages: -- gmake -- sqlite3 -- node -- boehm-gc -- pcre -- sfml -- sdl2 -- libffi - - -sources: -- https://github.com/nim-lang/Nim -environment: - NIM_TESTAMENT_BATCH: "0_2" - CC: /usr/bin/clang -tasks: -- setup: | - set -e - cd Nim - . ci/funs.sh && nimBuildCsourcesIfNeeded - echo 'export PATH=$HOME/Nim/bin:$PATH' >> $HOME/.buildenv -- test: | - set -e - cd Nim - . ci/funs.sh && nimInternalBuildKochAndRunCI -triggers: -- action: email - condition: failure - to: Andreas Rumpf diff --git a/.builds/openbsd_1.yml b/.builds/openbsd_1.yml deleted file mode 100644 index e98ec4640963..000000000000 --- a/.builds/openbsd_1.yml +++ /dev/null @@ -1,33 +0,0 @@ -## DO NO EDIT DIRECTLY! auto-generated by `nim r tools/ci_generate.nim` - -image: openbsd/latest -packages: -- gmake -- sqlite3 -- node -- boehm-gc -- pcre -- sfml -- sdl2 -- libffi - - -sources: -- https://github.com/nim-lang/Nim -environment: - NIM_TESTAMENT_BATCH: "1_2" - CC: /usr/bin/clang -tasks: -- setup: | - set -e - cd Nim - . ci/funs.sh && nimBuildCsourcesIfNeeded - echo 'export PATH=$HOME/Nim/bin:$PATH' >> $HOME/.buildenv -- test: | - set -e - cd Nim - . ci/funs.sh && nimInternalBuildKochAndRunCI -triggers: -- action: email - condition: failure - to: Andreas Rumpf diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md deleted file mode 100644 index 807e50d35c47..000000000000 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ /dev/null @@ -1,53 +0,0 @@ ---- -name: Bug report -about: Have you found an unexpected behavior? Use this template. -title: Think about the title, twice -labels: '' -assignees: '' - ---- - -(Consider writing a PR targetting devel branch after filing this, see [contributing.html](https://nim-lang.github.io/Nim/contributing.html)). - -Function `echo` outputs the wrong string. - -### Example -```nim -echo "Hello World!" -# This code should be a minimum reproducible example: -# try to simplify and minimize as much as possible. If it's a compiler -# issue, try to minimize further by removing any imports if possible. -``` - -### Current Output -please check whether the problem still exists in git head before posting, -see [rebuilding the compiler](https://nim-lang.github.io/Nim/intern.html#rebuilding-the-compiler). -``` -Hola mundo! -``` - -### Expected Output -``` -Hello World! -``` - -### Possible Solution - -* In file xyz there is a call that might be the cause of it. - -### Additional Information -If it's a regression, you can help us by identifying which version introduced -the bug, see [Bisecting for regressions](https://nim-lang.github.io/Nim/intern.html#bisecting-for-regressions), -or at least try known past releases (eg `choosenim 1.2.0`). - -If it's a pre-existing compiler bug, see [Debugging the compiler](https://nim-lang.github.io/Nim/intern.html#debugging-the-compiler) -which should give more context on a compiler crash. - -* Issue #abc is related, but different because of ... -* This issue is blocking my project xyz - -``` -$ nim -v -Nim Compiler Version 0.1.2 -# make sure to include the git hash if not using a tagged release -``` diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml new file mode 100644 index 000000000000..30b3d3351010 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -0,0 +1,75 @@ +name: "Bug Report" +description: "Create a new bug report. Have you found an unexpected behavior? Use this form." +title: "Think about the title, twice." +labels: ["unconfirmed"] +body: + +- type: markdown + attributes: + value: | + - **Please provide a minimal code example that reproduces the Bug!** :bug: + Reports with a reproducible example and descriptive detailed information will likely receive fixes faster. + +- type: textarea + id: what-happened + attributes: + label: What happened? + description: | + Use DETAILED DESCRIPTIVE information about the problem. + Here, you go into more details about your Bug report. This section can be a few paragraphs long. + placeholder: Bug reports with full repro code and detailed information will be fixed faster. + validations: + required: true + +- type: textarea + id: nim-version + attributes: + label: Nim Version + description: Please run `nim -v` on the command line. + validations: + required: true + +- type: textarea + id: current-logs + attributes: + label: Current Standard Output Logs + description: Please copy and paste any relevant log output. This will be automatically formatted into code, so no need for backticks. + placeholder: Bug reports with full repro code and detailed information will be fixed faster. + render: shell + +- type: textarea + id: expected-logs + attributes: + label: Expected Standard Output Logs + description: Please copy and paste any relevant log output. This will be automatically formatted into code, so no need for backticks. + placeholder: Bug reports with full repro code and detailed information will be fixed faster. + render: shell + +- type: textarea + id: possible-solution + attributes: + label: Possible Solution + description: Propose a possible solution. + validations: + required: false + +- type: textarea + id: extra-info + attributes: + label: Additional Information + description: Any additional relevant information. + validations: + required: false + +- type: markdown + attributes: + value: | + - Thanks for your contributions!, your Bug report will receive feedback from the community soon... + - Please check whether the problem still exists in the devel branch, see [rebuilding the compiler](https://nim-lang.github.io/Nim/intern.html#rebuilding-the-compiler). + - Consider writing a PR targetting devel branch after filing this, see [contributing](https://nim-lang.github.io/Nim/contributing.html). + - If it's a pre-existing compiler bug, see [Debugging the compiler](https://nim-lang.github.io/Nim/intern.html#debugging-the-compiler) + which should give more context on a compiler crash. + - If it's a regression, you can help us by identifying which version introduced the bug, + see [Bisecting for regressions](https://nim-lang.github.io/Nim/intern.html#bisecting-for-regressions), + or at least try known past releases (eg `choosenim 1.2.0`). + - [Please, consider a Donation for the Nim project.](https://nim-lang.org/donate.html) diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md deleted file mode 100644 index 6a9afe071356..000000000000 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ /dev/null @@ -1,35 +0,0 @@ ---- -name: Feature request -about: Do you want to suggest a new feature? Use this template. -title: '' -labels: ["Feature"] -assignees: '' - ---- - - - - -### Summary - - - -### Description - - - -### Alternatives - - - -### Additional Information - diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml new file mode 100644 index 000000000000..55b4836fd3bf --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -0,0 +1,73 @@ +name: "Feature request" +description: "Create a new Feature Request. Do you want to suggest a new feature? Use this form." +title: "Think about the title, twice." +labels: ["unconfirmed"] +body: + +- type: markdown + attributes: + value: | + - Please provide a minimal code example that illustrates the basic idea behind your feature request. + Reports with full repro code and descriptive detailed information will likely receive feedback faster. + - There is a separate repository for the detailed RFCs and proposals: https://github.com/nim-lang/RFCs. + If you have a simple feature request, you can post it here using this form, + but bear in mind that adding new features to the language is currently a low priority. + +- type: textarea + id: summary + attributes: + label: Summary + description: | + Use DETAILED DESCRIPTIVE information about the feature request. + Here, you go into more details about your ideas. This section can be a few paragraphs long. + placeholder: Short summary of your proposed feature. + validations: + required: true + +- type: textarea + id: description + attributes: + label: Description + description: Describe your solution, what problem does it fix? + validations: + required: true + +- type: textarea + id: alternatives + attributes: + label: Alternatives + description: Are there any alternatives you've considered? + validations: + required: false + +- type: textarea + id: Examples + attributes: + label: Standard Output Examples + description: Please copy and paste any relevant log output. This will be automatically formatted into code, so no need for backticks. + placeholder: Example code here. + render: shell + +- type: textarea + id: incompatibility + attributes: + label: Backwards Compatibility + description: If your Feature Request introduces backward-incompatible changes, describe them and propose how to deal with them. + validations: + required: false + +- type: textarea + id: links + attributes: + label: Links + description: Please copy and paste any relevant links to projects, issues, discussions, technical documentations, code samples, etc. + validations: + required: false + +- type: markdown + attributes: + value: | + - Thanks for your contributions!, your ideas will receive feedback from the community soon... + - **Remember to :star: Star the Nim project on GitHub!.** + - Consider writing a PR targetting devel branch after filing this, see [contributing](https://nim-lang.github.io/Nim/contributing.html). + - [Please, consider a Donation for the Nim project.](https://nim-lang.org/donate.html) diff --git a/.github/workflows/ci_docs.yml b/.github/workflows/ci_docs.yml index 6ae228d2d014..6228c48c8190 100644 --- a/.github/workflows/ci_docs.yml +++ b/.github/workflows/ci_docs.yml @@ -6,6 +6,7 @@ on: - 'compiler/renderverbatim.nim' - 'config/nimdoc.cfg' - 'doc/**.rst' + - 'doc/**.md' - 'doc/nimdoc.css' - 'lib/**.nim' - 'nimdoc/testproject/expected/testproject.html' @@ -20,6 +21,7 @@ on: - 'compiler/renderverbatim.nim' - 'config/nimdoc.cfg' - 'doc/**.rst' + - 'doc/**.md' - 'doc/nimdoc.css' - 'lib/**.nim' - 'nimdoc/testproject/expected/testproject.html' @@ -28,6 +30,10 @@ on: - '.github/workflows/ci_docs.yml' - 'koch.nim' +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + jobs: build: strategy: @@ -40,7 +46,7 @@ jobs: - target: windows os: windows-2019 - target: osx - os: macos-10.15 + os: macos-11 name: ${{ matrix.target }} runs-on: ${{ matrix.os }} @@ -48,7 +54,7 @@ jobs: steps: - name: 'Checkout' - uses: actions/checkout@v2 + uses: actions/checkout@v3 with: fetch-depth: 2 @@ -104,7 +110,7 @@ jobs: if: | github.event_name == 'push' && github.ref == 'refs/heads/devel' && matrix.target == 'linux' - uses: crazy-max/ghaction-github-pages@v1 + uses: crazy-max/ghaction-github-pages@v3 with: build_dir: doc/html env: diff --git a/.github/workflows/ci_packages.yml b/.github/workflows/ci_packages.yml index 529e6e8b6b09..86843d420730 100644 --- a/.github/workflows/ci_packages.yml +++ b/.github/workflows/ci_packages.yml @@ -1,12 +1,22 @@ name: Packages CI -on: [push, pull_request] +on: + pull_request: + push: + branches: + - 'devel' + - 'version-1-6' + - 'version-1-2' + +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true jobs: build: strategy: fail-fast: false matrix: - os: [ubuntu-20.04, macos-10.15] + os: [ubuntu-20.04, macos-11] cpu: [amd64] batch: ["allowed_failures", "0_3", "1_3", "2_3"] # list of `index_num` name: '${{ matrix.os }} (batch: ${{ matrix.batch }})' @@ -36,7 +46,8 @@ jobs: valgrind libc6-dbg libblas-dev xorg-dev - name: 'Install dependencies (macOS)' if: runner.os == 'macOS' - run: brew install boehmgc make sfml gtk+3 + run: | + brew install boehmgc make sfml gtk+3 - name: 'Install dependencies (Windows)' if: runner.os == 'Windows' shell: bash @@ -60,4 +71,5 @@ jobs: - name: 'koch, Run CI' shell: bash - run: . ci/funs.sh && nimInternalBuildKochAndRunCI + run: | + . ci/funs.sh && nimInternalBuildKochAndRunCI diff --git a/.github/workflows/ci_publish.yml b/.github/workflows/ci_publish.yml new file mode 100644 index 000000000000..a56ff9a7e541 --- /dev/null +++ b/.github/workflows/ci_publish.yml @@ -0,0 +1,90 @@ +name: Tracking orc-booting compiler memory usage + +on: + pull_request_target: + types: [closed] + + +jobs: + build: + if: github.event.pull_request.merged == true + strategy: + fail-fast: false + matrix: + os: [ubuntu-20.04] + cpu: [amd64] + name: '${{ matrix.os }}' + runs-on: ${{ matrix.os }} + steps: + - name: 'Checkout' + uses: actions/checkout@v2 + with: + fetch-depth: 2 + + - name: 'Install node.js 16.x' + uses: actions/setup-node@v2 + with: + node-version: '16.x' + + - name: 'Install dependencies (Linux amd64)' + if: runner.os == 'Linux' && matrix.cpu == 'amd64' + run: | + sudo apt-fast update -qq + DEBIAN_FRONTEND='noninteractive' \ + sudo apt-fast install --no-install-recommends -yq \ + libcurl4-openssl-dev libgc-dev libsdl1.2-dev libsfml-dev \ + valgrind libc6-dbg libblas-dev xorg-dev + - name: 'Install dependencies (macOS)' + if: runner.os == 'macOS' + run: brew install boehmgc make sfml gtk+3 + - name: 'Install dependencies (Windows)' + if: runner.os == 'Windows' + shell: bash + run: | + set -e + . ci/funs.sh + nimInternalInstallDepsWindows + echo_run echo "${{ github.workspace }}/dist/mingw64/bin" >> "${GITHUB_PATH}" + + - name: 'Add build binaries to PATH' + shell: bash + run: echo "${{ github.workspace }}/bin" >> "${GITHUB_PATH}" + + - name: 'System information' + shell: bash + run: . ci/funs.sh && nimCiSystemInfo + + - name: 'Build csourcesAny' + shell: bash + run: . ci/funs.sh && nimBuildCsourcesIfNeeded CC=gcc ucpu='${{ matrix.cpu }}' + + - name: 'Build koch' + shell: bash + run: nim c koch + + - name: 'Build Nim' + shell: bash + run: ./koch boot -d:release -d:nimStrictMode --lib:lib + + - name: 'Action' + shell: bash + run: nim c -r -d:release ci/action.nim + + - name: 'Comment' + uses: actions/github-script@v6 + with: + script: | + const fs = require('fs'); + + try { + const data = fs.readFileSync('ci/nimcache/results.txt', 'utf8'); + github.rest.issues.createComment({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + body: data + }) + } catch (err) { + console.error(err); + } + diff --git a/azure-pipelines.yml b/azure-pipelines.yml index a70a86faeed6..d034e3b9bad4 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -1,7 +1,8 @@ trigger: branches: include: - - '*' + - 'devel' + - 'version-*' pr: branches: include: @@ -19,7 +20,7 @@ jobs: strategy: matrix: Linux_amd64: - vmImage: 'ubuntu-18.04' + vmImage: 'ubuntu-20.04' CPU: amd64 # regularly breaks, refs bug #17325 Linux_i386: @@ -28,10 +29,10 @@ jobs: vmImage: 'ubuntu-18.04' CPU: i386 OSX_amd64: - vmImage: 'macOS-10.15' + vmImage: 'macOS-11' CPU: amd64 OSX_amd64_cpp: - vmImage: 'macOS-10.15' + vmImage: 'macOS-11' CPU: amd64 NIM_COMPILE_TO_CPP: true Windows_amd64_batch0_3: diff --git a/build_all.bat b/build_all.bat index c8ba05e19b19..ef4a5f6a4e78 100644 --- a/build_all.bat +++ b/build_all.bat @@ -24,6 +24,6 @@ if not exist %nim_csources% ( cd .. copy /y bin\nim.exe %nim_csources% ) - bin\nim.exe c --skipUserCfg --skipParentCfg --hints:off koch + bin\nim.exe c --noNimblePath --skipUserCfg --skipParentCfg --hints:off koch koch boot -d:release --skipUserCfg --skipParentCfg --hints:off koch tools --skipUserCfg --skipParentCfg --hints:off diff --git a/build_all.sh b/build_all.sh index fb3d15d45575..83848f41a1de 100755 --- a/build_all.sh +++ b/build_all.sh @@ -11,7 +11,7 @@ set -e # exit on first error . ci/funs.sh nimBuildCsourcesIfNeeded "$@" -echo_run bin/nim c --skipUserCfg --skipParentCfg --hints:off koch +echo_run bin/nim c --noNimblePath --skipUserCfg --skipParentCfg --hints:off koch echo_run ./koch boot -d:release --skipUserCfg --skipParentCfg --hints:off echo_run ./koch tools --skipUserCfg --skipParentCfg --hints:off diff --git a/changelog.md b/changelog.md index c87f40ad0475..1fca507d4e19 100644 --- a/changelog.md +++ b/changelog.md @@ -2,98 +2,214 @@ ## Changes affecting backward compatibility - -- The `Math.trunc` polyfill for targeting Internet Explorer was - previously emitted for every JavaScript output file except if - the symbol `nodejs` was defined via `-d:nodejs`. Now, it is only - emitted if the symbol `nimJsMathTruncPolyfill` is defined. If you are - targeting Internet Explorer, you may choose to enable this option - or define your own `Math.trunc` polyfill using the [`emit` pragma](https://nim-lang.org/docs/manual.html#implementation-specific-pragmas-emit-pragma). Nim uses - `Math.trunc` for the division and modulo operators for integers. +- `httpclient.contentLength` default to `-1` if the Content-Length header is not set in the response, it followed Apache HttpClient(Java), http(go) and .Net HttpWebResponse(C#) behavior. Previously raise `ValueError`. + +- `addr` is now available for all addressable locations, + `unsafeAddr` is now deprecated and an alias for `addr`. + +- Certain definitions from the default `system` module have been moved to + the following new modules: + + - `std/syncio` + - `std/assertions` + - `std/formatfloat` + - `std/objectdollar` + + In the future, these definitions will be removed from the `system` module, + and their respective modules will have to be imported to use them. + Currently, to make these imports required, the `-d:nimPreviewSlimSystem` option + may be used. + +- Enabling `-d:nimPreviewSlimSystem` also removes the following deprecated + symbols in the `system` module: + - Aliases with `Error` suffix to exception types that have a `Defect` suffix + (see [exceptions](https://nim-lang.org/docs/exceptions.html)): + `ArithmeticError`, `DivByZeroError`, `OverflowError`, + `AccessViolationError`, `AssertionError`, `OutOfMemError`, `IndexError`, + `FieldError`, `RangeError`, `StackOverflowError`, `ReraiseError`, + `ObjectAssignmentError`, `ObjectConversionError`, `FloatingPointError`, + `FloatOverflowError`, `FloatUnderflowError`, `FloatInexactError`, + `DeadThreadError`, `NilAccessError` + - `addQuitProc`, replaced by `exitprocs.addExitProc` + - Legacy unsigned conversion operations: `ze`, `ze64`, `toU8`, `toU16`, `toU32` + - `TaintedString`, formerly a distinct alias to `string` + - `PInt32`, `PInt64`, `PFloat32`, `PFloat64`, aliases to + `ptr int32`, `ptr int64`, `ptr float32`, `ptr float64` + +- The `gc:v2` option is removed. + +- The `mainmodule` and `m` options are removed. + +- The `threads:on` option is now the default. - Optional parameters in combination with `: body` syntax (RFC #405) are now opt-in via `experimental:flexibleOptionalParams`. -- `std/sharedstrings` module is removed. -- Constants `colors.colPaleVioletRed` and `colors.colMediumPurple` changed to match the CSS color standard. +- The `Math.trunc` polyfill for targeting Internet Explorer was + previously included in most JavaScript output files. + Now, it is only included with `-d:nimJsMathTruncPolyfill`. + If you are targeting Internet Explorer, you may choose to enable this option + or define your own `Math.trunc` polyfill using the [`emit` pragma](https://nim-lang.org/docs/manual.html#implementation-specific-pragmas-emit-pragma). + Nim uses `Math.trunc` for the division and modulo operators for integers. + +- `shallowCopy` is removed for ARC/ORC. Use `move` when possible or combine assignment and +`sink` for optimization purposes. + +- The `nimPreviewDotLikeOps` define is going to be removed or deprecated. + +- The `{.this.}` pragma, deprecated since 0.19, has been removed. +- `nil` literals can no longer be directly assigned to variables or fields of `distinct` pointer types. They must be converted instead. + ```nim + type Foo = distinct ptr int + + # Before: + var x: Foo = nil + # After: + var x: Foo = Foo(nil) + ``` +- Removed two type pragma syntaxes deprecated since 0.20, namely + `type Foo = object {.final.}`, and `type Foo {.final.} [T] = object`. + +- [Overloadable enums](https://nim-lang.github.io/Nim/manual.html#overloadable-enum-value-names) and Unicode Operators + are no longer experimental. -- `addr` is now available for all addressable locations, `unsafeAddr` is deprecated and -becomes an alias for `addr`. +- Removed the `nimIncrSeqV3` define. -- io is about to move out of system; use `-d:nimPreviewSlimSystem` and import `std/syncio`. +- Static linking against OpenSSL versions below 1.1, previously done by + setting `-d:openssl10`, is no longer supported. + +- `macros.getImpl` for `const` symbols now returns the full definition node + (as `nnkConstDef`) rather than the AST of the constant value. + +- ORC is now the default memory management strategy. Use + `--mm:refc` for a transition period. ## Standard library additions and changes +[//]: # "Changes:" +- OpenSSL version 3 is now supported by setting either `-d:sslVersion=3` or `-d:useOpenssl3`. - `macros.parseExpr` and `macros.parseStmt` now accept an optional filename argument for more informative errors. - Module `colors` expanded with missing colors from the CSS color standard. + `colPaleVioletRed` and `colMediumPurple` have also been changed to match the CSS color standard. - Fixed `lists.SinglyLinkedList` being broken after removing the last node ([#19353](https://github.com/nim-lang/Nim/pull/19353)). -- `md5` now works at compile time and in JavaScript. - +- The `md5` module now works at compile time and in JavaScript. - `std/smtp` sends `ehlo` first. If the mail server does not understand, it sends `helo` as a fallback. - -- Added `IsoWeekRange`, a range type to represent the number of weeks in an ISO week-based year. -- Added `IsoYear`, a distinct int type to prevent bugs from confusing the week-based year and the regular year. -- Added `initDateTime` in `times` to create a datetime from a weekday, and ISO 8601 week number and week-based year. -- Added `getIsoWeekAndYear` in `times` to get an ISO week number along with the corresponding ISO week-based year from a datetime. -- Added `getIsoWeeksInYear` in `times` to return the number of weeks in an ISO week-based year. - +- Changed `mimedb` to use an `OrderedTable` instead of `OrderedTableRef` to support `const` tables. +- `strutils.find` now uses and defaults to `last = -1` for whole string searches, + making limiting it to just the first char (`last = 0`) valid. +- `random.rand` now works with `Ordinal`s. +- `std/oids` now uses `int64` to store time internally (before it was int32), the length of + the string form of `Oid` changes from 24 to 32. + +[//]: # "Additions:" +- Added ISO 8601 week date utilities in `times`: + - Added `IsoWeekRange`, a range type for weeks in a week-based year. + - Added `IsoYear`, a distinct type for a week-based year in contrast to a regular year. + - Added a `initDateTime` overload to create a datetime from an ISO week date. + - Added `getIsoWeekAndYear` to get an ISO week number and week-based year from a datetime. + - Added `getIsoWeeksInYear` to return the number of weeks in a week-based year. - Added `std/oserrors` for OS error reporting. Added `std/envvars` for environment variables handling. +- Added `sep` parameter in `std/uri` to specify the query separator. +- Added bindings to [`Array.shift`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/shift) + and [`queueMicrotask`](https://developer.mozilla.org/en-US/docs/Web/API/queueMicrotask) + in `jscore` for JavaScript targets. +- Added `UppercaseLetters`, `LowercaseLetters`, `PunctuationChars`, `PrintableChars` sets to `std/strutils`. +- Added `complex.sgn` for obtaining the phase of complex numbers. +- Added `insertAdjacentText`, `insertAdjacentElement`, `insertAdjacentHTML`, + `after`, `before`, `closest`, `append`, `hasAttributeNS`, `removeAttributeNS`, + `hasPointerCapture`, `releasePointerCapture`, `requestPointerLock`, + `replaceChildren`, `replaceWith`, `scrollIntoViewIfNeeded`, `setHTML`, + `toggleAttribute`, and `matches` to `std/dom`. +- Added [`jsre.hasIndices`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/hasIndices) +- Added `capacity` for `string` and `seq` to return the current capacity, see https://github.com/nim-lang/RFCs/issues/460 + +[//]: # "Deprecations:" +- Deprecated `selfExe` for Nimscript. +- Deprecated `std/sums`. + +[//]: # "Removals:" +- Removed deprecated module `parseopt2`. +- Removed deprecated module `sharedstrings`. +- Removed deprecated module `dom_extensions`. +- Removed deprecated module `LockFreeHash`. +- Removed deprecated module `events`. - Removed deprecated `oids.oidToString`. -- Remove define `nimExperimentalAsyncjsThen` for `std/asyncjs.then` and `std/jsfetch`. - -- Changed mimedb to use an `OrderedTable` instead of `OrderedTableRef`, to use it in a const. +- Removed define `nimExperimentalAsyncjsThen` for `std/asyncjs.then` and `std/jsfetch`. - Removed deprecated `jsre.test` and `jsre.toString`. - Removed deprecated `math.c_frexp`. -- Removed deprecated ``` httpcore.`==` ```. -- Removed deprecated `std/dom_extensions`. - -- Remove deprecated `osproc.poDemon`, symbol with typo. - -- Deprecated `selfExe` for Nimscript. - -- `std/syncio` no longer attempts to lock stdout on the DOS target. +- Removed deprecated `` httpcore.`==` ``. +- Removed deprecated `std/posix.CMSG_SPACE` and `std/posix.CMSG_LEN` that takes wrong argument types. +- Removed deprecated `osproc.poDemon`, symbol with typo. ## Language changes -- Pragma macros on type definitions can now return `nnkTypeSection` nodes as well as `nnkTypeDef`, - allowing multiple type definitions to be injected in place of the original type definition. - - ```nim - import macros - - macro multiply(amount: static int, s: untyped): untyped = - let name = $s[0].basename - result = newNimNode(nnkTypeSection) - for i in 1 .. amount: - result.add(newTree(nnkTypeDef, ident(name & $i), s[1], s[2])) - - type - Foo = object - Bar {.multiply: 3.} = object - x, y, z: int - Baz = object - - # becomes - - type - Foo = object - Bar1 = object - x, y, z: int - Bar2 = object - x, y, z: int - Bar3 = object - x, y, z: int - Baz = object - ``` -- [Case statement macros](manual.html#macros-case-statement-macros) are no longer experimental, +- [Tag tracking](https://nim-lang.github.io/Nim/manual.html#effect-system-tag-tracking) supports the definition of forbidden tags by the `.forbids` pragma + which can be used to disable certain effects in proc types. +- [Case statement macros](https://nim-lang.github.io/Nim/manual.html#macros-case-statement-macros) are no longer experimental, meaning you no longer need to enable the experimental switch `caseStmtMacros` to use them. - Full command syntax and block arguments i.e. `foo a, b: c` are now allowed for the right-hand side of type definitions in type sections. Previously they would error with "invalid indentation". +- `defined` now accepts identifiers separated by dots, i.e. `defined(a.b.c)`. + In the command line, this is defined as `-d:a.b.c`. Older versions can + use accents as in ``defined(`a.b.c`)`` to access such defines. +- [Macro pragmas](https://nim-lang.github.io/Nim/manual.html#userminusdefined-pragmas-macro-pragmas) changes: + - Templates now accept macro pragmas. + - Macro pragmas for var/let/const sections have been redesigned in a way that works + similarly to routine macro pragmas. The new behavior is documented in the + [experimental manual](https://nim-lang.github.io/Nim/manual_experimental.html#extended-macro-pragmas). + - Pragma macros on type definitions can now return `nnkTypeSection` nodes as well as `nnkTypeDef`, + allowing multiple type definitions to be injected in place of the original type definition. + + ```nim + import macros + macro multiply(amount: static int, s: untyped): untyped = + let name = $s[0].basename + result = newNimNode(nnkTypeSection) + for i in 1 .. amount: + result.add(newTree(nnkTypeDef, ident(name & $i), s[1], s[2])) + type + Foo = object + Bar {.multiply: 3.} = object + x, y, z: int + Baz = object + # becomes + type + Foo = object + Bar1 = object + x, y, z: int + Bar2 = object + x, y, z: int + Bar3 = object + x, y, z: int + Baz = object + ``` + +- Redefining templates with the same signature implicitly was previously + allowed to support certain macro code. A `{.redefine.}` pragma has been + added to make this work explicitly, and a warning is generated in the case + where it is implicit. This behavior only applies to templates, redefinition + is generally disallowed for other symbols. + +- A new form of type inference called [top-down inference](https://nim-lang.github.io/Nim/manual_experimental.html#topminusdown-type-inference) + has been implemented for a variety of basic cases. For example, code like the following now compiles: + + ```nim + let foo: seq[(float, byte, cstring)] = @[(1, 2, "abc")] + ``` + +- `cstring` is now accepted as a selector in `case` statements, removing the + need to convert to `string`. On the JS backend, this is translated directly + to a `switch` statement. ## Compiler changes +- The `gc` switch has been renamed to `mm` ("memory management") in order to reflect the + reality better. (Nim moved away from all techniques based on "tracing".) + +- Defines the `gcRefc` symbol which allows writing specific code for the refc GC. + - `nim` can now compile version 1.4.0 as follows: `nim c --lib:lib --stylecheck:off compiler/nim`, without requiring `-d:nimVersion140` which is now a noop. @@ -103,14 +219,15 @@ becomes an alias for `addr`. - Sample cross-compilation configuration for DOS on i086 and i086big with the OpenWatcom C compiler has been added to `$nim/config/nim.cfg`. -## Tool changes +- `--styleCheck`, `--hintAsError` and `--warningAsError` now only applies to the current package. -- The `gc` switch has been renamed to `mm` ("memory management") in order to reflect the - reality better. (Nim moved away from all techniques based on "tracing".) +- The switch `--nimMainPrefix:prefix` has been added to add a prefix to the names of `NimMain` and + related functions produced on the backend. This prevents conflicts with other Nim + static libraries. + + +## Tool changes - Nim now supports Nimble version 0.14 which added support for lock-files. This is done by a simple configuration change setting that you can do yourself too. In `$nim/config/nim.cfg` replace `pkgs` by `pkgs2`. - -- There is a new switch `--nimMainPrefix:prefix` to influence the `NimMain` that the - compiler produces. This is particularly useful for generating static libraries. diff --git a/ci/action.nim b/ci/action.nim new file mode 100644 index 000000000000..5d3a50fda24d --- /dev/null +++ b/ci/action.nim @@ -0,0 +1,26 @@ +import std/[strutils, os, osproc, parseutils, strformat] + + +proc main() = + var msg = "" + const cmd = "./koch boot --gc:orc -d:release" + + let (output, exitCode) = execCmdEx(cmd) + + doAssert exitCode == 0, output + + let start = rfind(output, "Hint: mm") + doAssert parseUntil(output, msg, "; proj", start) > 0, output + + let (commitHash, _) = execCmdEx("""git log --format="%H" -n 1""") + + let welcomeMessage = fmt"""Thanks for your hard work on this PR! +The lines below are statistics of the Nim compiler built from {commitHash} + +{msg} +""" + createDir "ci/nimcache" + writeFile "ci/nimcache/results.txt", welcomeMessage + +when isMainModule: + main() diff --git a/compiler.nimble b/compiler.nimble deleted file mode 100644 index f4da45519d82..000000000000 --- a/compiler.nimble +++ /dev/null @@ -1,9 +0,0 @@ - -version = system.NimVersion -author = "Andreas Rumpf" -description = "Compiler package providing the compiler sources as a library." -license = "MIT" - -installDirs = @["compiler", "nimsuggest"] - -requires "nim >= 0.14.0" diff --git a/compiler/aliases.nim b/compiler/aliases.nim index 9ec72faa46db..4b50fdb28294 100644 --- a/compiler/aliases.nim +++ b/compiler/aliases.nim @@ -77,24 +77,29 @@ proc isPartOf*(a, b: PNode): TAnalysisResult = ## cases: ## ## YES-cases: + ## ``` ## x <| x # for general trees ## x[] <| x ## x[i] <| x ## x.f <| x + ## ``` ## ## NO-cases: + ## ``` ## x !<| y # depending on type and symbol kind ## x[constA] !<| x[constB] ## x.f !<| x.g ## x.f !<| y.f iff x !<= y + ## ``` ## ## MAYBE-cases: ## + ## ``` ## x[] ?<| y[] iff compatible type ## ## ## x[] ?<| y depending on type - ## + ## ``` if a.kind == b.kind: case a.kind of nkSym: diff --git a/compiler/ast.nim b/compiler/ast.nim index d1e5ae2bfe65..399d7e5613e1 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -232,7 +232,7 @@ type TNodeKinds* = set[TNodeKind] type - TSymFlag* = enum # 48 flags! + TSymFlag* = enum # 49 flags! sfUsed, # read access of sym (for warnings) or simply used sfExported, # symbol is exported from module sfFromGeneric, # symbol is instantiation of a generic; this is needed @@ -260,7 +260,8 @@ type # *OR*: a proc is indirectly called (used as first class) sfCompilerProc, # proc is a compiler proc, that is a C proc that is # needed for the code generator - sfProcvar, # proc can be passed to a proc var + sfEscapes # param escapes + # currently unimplemented sfDiscriminant, # field is a discriminant in a record/object sfRequiresInit, # field must be initialized during construction sfDeprecated, # symbol is deprecated @@ -303,6 +304,12 @@ type sfSingleUsedTemp # For temporaries that we know will only be used once sfNoalias # 'noalias' annotation, means C's 'restrict' sfEffectsDelayed # an 'effectsDelayed' parameter + sfGeneratedType # A anonymous generic type that is generated by the compiler for + # objects that do not have generic parameters in case one of the + # object fields has one. + # + # This is disallowed but can cause the typechecking to go into + # an infinite loop, this flag is used as a sentinel to stop it. TSymFlags* = set[TSymFlag] @@ -331,10 +338,10 @@ const sfExperimental* = sfOverriden # module uses the .experimental switch sfGoto* = sfOverriden # var is used for 'goto' code generation sfWrittenTo* = sfBorrow # param is assigned to - sfEscapes* = sfProcvar # param escapes + # currently unimplemented sfBase* = sfDiscriminant - sfIsSelf* = sfOverriden # param is 'self' sfCustomPragma* = sfRegister # symbol is custom pragma template + sfTemplateRedefinition* = sfExportc # symbol is a redefinition of an earlier template const # getting ready for the future expr/stmt merge @@ -347,7 +354,8 @@ const ensuresEffects* = 2 # 'ensures' annotation tagEffects* = 3 # user defined tag ('gc', 'time' etc.) pragmasEffects* = 4 # not an effect, but a slot for pragmas in proc type - effectListLen* = 5 # list of effects list + forbiddenEffects* = 5 # list of illegal effects + effectListLen* = 6 # list of effects list nkLastBlockStmts* = {nkRaiseStmt, nkReturnStmt, nkBreakStmt, nkContinueStmt} # these must be last statements in a block @@ -673,7 +681,7 @@ type mInSet, mRepr, mExit, mSetLengthStr, mSetLengthSeq, mIsPartOf, mAstToStr, mParallel, - mSwap, mIsNil, mArrToSeq, + mSwap, mIsNil, mArrToSeq, mOpenArrayToSeq, mNewString, mNewStringOfCap, mParseBiggestFloat, mMove, mWasMoved, mDestroy, mTrace, mDefault, mUnown, mFinished, mIsolate, mAccessEnv, mAccessTypeField, mReset, @@ -707,8 +715,8 @@ type mSymIsInstantiationOf, mNodeId, mPrivateAccess -# things that we can evaluate safely at compile time, even if not asked for it: const + # things that we can evaluate safely at compile time, even if not asked for it: ctfeWhitelist* = {mNone, mSucc, mPred, mInc, mDec, mOrd, mLengthOpenArray, mLengthStr, mLengthArray, mLengthSeq, @@ -736,13 +744,19 @@ const mEqStr, mLeStr, mLtStr, mEqSet, mLeSet, mLtSet, mMulSet, mPlusSet, mMinusSet, mConStrStr, mAppendStrCh, mAppendStrStr, mAppendSeqElem, - mInSet, mRepr} + mInSet, mRepr, mOpenArrayToSeq} + + generatedMagics* = {mNone, mIsolate, mFinished, mOpenArrayToSeq} + ## magics that are generated as normal procs in the backend type ItemId* = object module*: int32 item*: int32 +proc `$`*(x: ItemId): string = + "(module: " & $x.module & ", item: " & $x.item & ")" + proc `==`*(a, b: ItemId): bool {.inline.} = a.item == b.item and a.module == b.module @@ -1108,21 +1122,6 @@ proc getPIdent*(a: PNode): PIdent {.inline.} = of nkIdent: a.ident else: nil -proc getnimblePkg*(a: PSym): PSym = - result = a - while result != nil: - case result.kind - of skModule: - result = result.owner - assert result.kind == skPackage - of skPackage: - if result.owner == nil: - break - else: - result = result.owner - else: - assert false, $result.kind - const moduleShift = when defined(cpu32): 20 else: 24 @@ -1167,13 +1166,7 @@ when false: assert dest.ItemId.item <= src.ItemId.item dest = src -proc getnimblePkgId*(a: PSym): int = - let b = a.getnimblePkg - result = if b == nil: -1 else: b.id - var ggDebug* {.deprecated.}: bool ## convenience switch for trying out things -#var -# gMainPackageId*: int proc isCallExpr*(n: PNode): bool = result = n.kind in nkCallKinds @@ -1240,6 +1233,25 @@ proc getDeclPragma*(n: PNode): PNode = if result != nil: assert result.kind == nkPragma, $(result.kind, n.kind) +proc extractPragma*(s: PSym): PNode = + ## gets the pragma node of routine/type/var/let/const symbol `s` + if s.kind in routineKinds: + result = s.ast[pragmasPos] + elif s.kind in {skType, skVar, skLet, skConst}: + if s.ast != nil and s.ast.len > 0: + if s.ast[0].kind == nkPragmaExpr and s.ast[0].len > 1: + # s.ast = nkTypedef / nkPragmaExpr / [nkSym, nkPragma] + result = s.ast[0][1] + assert result == nil or result.kind == nkPragma + +proc skipPragmaExpr*(n: PNode): PNode = + ## if pragma expr, give the node the pragmas are applied to, + ## otherwise give node itself + if n.kind == nkPragmaExpr: + result = n[0] + else: + result = n + when defined(useNodeIds): const nodeIdToDebug* = -1 # 2322968 var gNodeId: int @@ -1328,7 +1340,7 @@ proc newSym*(symKind: TSymKind, name: PIdent, id: ItemId, owner: PSym, proc astdef*(s: PSym): PNode = # get only the definition (initializer) portion of the ast - if s.ast != nil and s.ast.kind == nkIdentDefs: + if s.ast != nil and s.ast.kind in {nkIdentDefs, nkConstDef}: s.ast[2] else: s.ast @@ -1511,7 +1523,7 @@ proc mergeLoc(a: var TLoc, b: TLoc) = if a.storage == low(typeof(a.storage)): a.storage = b.storage a.flags.incl b.flags if a.lode == nil: a.lode = b.lode - if a.r == nil: a.r = b.r + if a.r == "": a.r = b.r proc newSons*(father: Indexable, length: int) = setLen(father.sons, length) @@ -1527,7 +1539,7 @@ proc assignType*(dest, src: PType) = # this fixes 'type TLock = TSysLock': if src.sym != nil: if dest.sym != nil: - dest.sym.flags.incl src.sym.flags-{sfExported} + dest.sym.flags.incl src.sym.flags-{sfUsed, sfExported} if dest.sym.annex == nil: dest.sym.annex = src.sym.annex mergeLoc(dest.sym.loc, src.sym.loc) else: @@ -1697,6 +1709,10 @@ proc transitionIntKind*(n: PNode, kind: range[nkCharLit..nkUInt64Lit]) = transitionNodeKindCommon(kind) n.intVal = obj.intVal +proc transitionIntToFloatKind*(n: PNode, kind: range[nkFloatLit..nkFloat128Lit]) = + transitionNodeKindCommon(kind) + n.floatVal = BiggestFloat(obj.intVal) + proc transitionNoneToSym*(n: PNode) = transitionNodeKindCommon(nkSym) diff --git a/compiler/astalgo.nim b/compiler/astalgo.nim index bc5b7d6e178e..28c38129ebeb 100644 --- a/compiler/astalgo.nim +++ b/compiler/astalgo.nim @@ -12,12 +12,17 @@ # the data structures here are used in various places of the compiler. import - ast, hashes, intsets, strutils, options, lineinfos, ropes, idents, rodutils, + ast, hashes, intsets, options, lineinfos, ropes, idents, rodutils, msgs +import strutils except addf + when defined(nimPreviewSlimSystem): import std/assertions +when not defined(nimHasCursor): + {.pragma: cursor.} + proc hashNode*(p: RootRef): Hash proc treeToYaml*(conf: ConfigRef; n: PNode, indent: int = 0, maxRecDepth: int = - 1): Rope # Convert a tree into its YAML representation; this is used by the @@ -224,11 +229,10 @@ proc getNamedParamFromList*(list: PNode, ident: PIdent): PSym = ## Named parameters are special because a named parameter can be ## gensym'ed and then they have '\`' suffix that we need to ## ignore, see compiler / evaltempl.nim, snippet: - ## - ## .. code-block:: nim - ## + ## ``` ## result.add newIdentNode(getIdent(c.ic, x.name.s & "\`gensym" & $x.id), ## if c.instLines: actual.info else: templ.info) + ## ``` for i in 1.. 0: @@ -215,7 +217,7 @@ proc openArrayLoc(p: BProc, formalType: PType, n: PNode): Rope = genStmts(p, q[i]) q = q.lastSon let (x, y) = genOpenArraySlice(p, q, formalType, n.typ[0]) - result = x & ", " & y + result.add x & ", " & y else: var a: TLoc initLocExpr(p, if n.kind == nkHiddenStdConv: n[1] else: n, a) @@ -223,11 +225,11 @@ proc openArrayLoc(p: BProc, formalType: PType, n: PNode): Rope = of tyOpenArray, tyVarargs: if reifiedOpenArray(n): if a.t.kind in {tyVar, tyLent}: - result = "$1->Field0, $1->Field1" % [rdLoc(a)] + result.add "$1->Field0, $1->Field1" % [rdLoc(a)] else: - result = "$1.Field0, $1.Field1" % [rdLoc(a)] + result.add "$1.Field0, $1.Field1" % [rdLoc(a)] else: - result = "$1, $1Len_0" % [rdLoc(a)] + result.add "$1, $1Len_0" % [rdLoc(a)] of tyString, tySequence: let ntyp = skipTypes(n.typ, abstractInst) if formalType.skipTypes(abstractInst).kind in {tyVar} and ntyp.kind == tyString and @@ -236,19 +238,19 @@ proc openArrayLoc(p: BProc, formalType: PType, n: PNode): Rope = if ntyp.kind in {tyVar} and not compileToCpp(p.module): var t: TLoc t.r = "(*$1)" % [a.rdLoc] - result = "(*$1)$3, $2" % [a.rdLoc, lenExpr(p, t), dataField(p)] + result.add "(*$1)$3, $2" % [a.rdLoc, lenExpr(p, t), dataField(p)] else: - result = "$1$3, $2" % [a.rdLoc, lenExpr(p, a), dataField(p)] + result.add "$1$3, $2" % [a.rdLoc, lenExpr(p, a), dataField(p)] of tyArray: - result = "$1, $2" % [rdLoc(a), rope(lengthOrd(p.config, a.t))] + result.add "$1, $2" % [rdLoc(a), rope(lengthOrd(p.config, a.t))] of tyPtr, tyRef: case lastSon(a.t).kind of tyString, tySequence: var t: TLoc t.r = "(*$1)" % [a.rdLoc] - result = "(*$1)$3, $2" % [a.rdLoc, lenExpr(p, t), dataField(p)] + result.add "(*$1)$3, $2" % [a.rdLoc, lenExpr(p, t), dataField(p)] of tyArray: - result = "$1, $2" % [rdLoc(a), rope(lengthOrd(p.config, lastSon(a.t)))] + result.add "$1, $2" % [rdLoc(a), rope(lengthOrd(p.config, lastSon(a.t)))] else: internalError(p.config, "openArrayLoc: " & typeToString(a.t)) else: internalError(p.config, "openArrayLoc: " & typeToString(a.t)) @@ -268,24 +270,24 @@ proc literalsNeedsTmp(p: BProc, a: TLoc): TLoc = getTemp(p, a.lode.typ, result, needsInit=false) genAssignment(p, result, a, {}) -proc genArgStringToCString(p: BProc, n: PNode, needsTmp: bool): Rope {.inline.} = +proc genArgStringToCString(p: BProc, n: PNode; result: var Rope; needsTmp: bool) {.inline.} = var a: TLoc initLocExpr(p, n[0], a) - ropecg(p.module, "#nimToCStringConv($1)", [withTmpIfNeeded(p, a, needsTmp).rdLoc]) + appcg(p.module, result, "#nimToCStringConv($1)", [withTmpIfNeeded(p, a, needsTmp).rdLoc]) -proc genArg(p: BProc, n: PNode, param: PSym; call: PNode, needsTmp = false): Rope = +proc genArg(p: BProc, n: PNode, param: PSym; call: PNode; result: var Rope; needsTmp = false) = var a: TLoc if n.kind == nkStringToCString: - result = genArgStringToCString(p, n, needsTmp) + genArgStringToCString(p, n, result, needsTmp) elif skipTypes(param.typ, abstractVar).kind in {tyOpenArray, tyVarargs}: var n = if n.kind != nkHiddenAddr: n else: n[0] - result = openArrayLoc(p, param.typ, n) + openArrayLoc(p, param.typ, n, result) elif ccgIntroducedPtr(p.config, param, call[0].typ[0]): initLocExpr(p, n, a) if n.kind in {nkCharLit..nkNilLit}: - result = addrLoc(p.config, literalsNeedsTmp(p, a)) + addAddrLoc(p.config, literalsNeedsTmp(p, a), result) else: - result = addrLoc(p.config, withTmpIfNeeded(p, a, needsTmp)) + addAddrLoc(p.config, withTmpIfNeeded(p, a, needsTmp), result) elif p.module.compileToCpp and param.typ.kind in {tyVar} and n.kind == nkHiddenAddr: initLocExprSingleUse(p, n[0], a) @@ -295,21 +297,21 @@ proc genArg(p: BProc, n: PNode, param: PSym; call: PNode, needsTmp = false): Rop if callee.kind == nkSym and {sfImportc, sfInfixCall, sfCompilerProc} * callee.sym.flags == {sfImportc} and {lfHeader, lfNoDecl} * callee.sym.loc.flags != {}: - result = addrLoc(p.config, a) + addAddrLoc(p.config, a, result) else: - result = rdLoc(a) + addRdLoc(a, result) else: initLocExprSingleUse(p, n, a) - result = rdLoc(withTmpIfNeeded(p, a, needsTmp)) + addRdLoc(withTmpIfNeeded(p, a, needsTmp), result) #assert result != nil -proc genArgNoParam(p: BProc, n: PNode, needsTmp = false): Rope = +proc genArgNoParam(p: BProc, n: PNode; result: var Rope; needsTmp = false) = var a: TLoc if n.kind == nkStringToCString: - result = genArgStringToCString(p, n, needsTmp) + genArgStringToCString(p, n, result, needsTmp) else: initLocExprSingleUse(p, n, a) - result = rdLoc(withTmpIfNeeded(p, a, needsTmp)) + addRdLoc(withTmpIfNeeded(p, a, needsTmp), result) from dfa import aliases, AliasKind @@ -365,7 +367,7 @@ proc getPotentialReads(n: PNode; result: var seq[PNode]) = for s in n: getPotentialReads(s, result) -proc genParams(p: BProc, ri: PNode, typ: PType): Rope = +proc genParams(p: BProc, ri: PNode, typ: PType; result: var Rope) = # We must generate temporaries in cases like #14396 # to keep the strict Left-To-Right evaluation var needTmp = newSeq[bool](ri.len - 1) @@ -385,16 +387,21 @@ proc genParams(p: BProc, ri: PNode, typ: PType): Rope = # Optimization: don't use a temp, if we would only take the address anyway needTmp[i - 1] = false + var oldLen = result.len for i in 1.. 1: pl.add(~", ") + if ri.len > 1: pl.add(", ") # beware of 'result = p(result)'. We may need to allocate a temporary: if d.k in {locTemp, locNone} or not preventNrvo(p, d.lode, le, ri): # Great, we can use 'd': @@ -491,24 +500,30 @@ proc genClosureCall(p: BProc, le, ri: PNode, d: var TLoc) = genCallPattern() if canRaise: raiseExit(p) -proc genOtherArg(p: BProc; ri: PNode; i: int; typ: PType): Rope = +proc genOtherArg(p: BProc; ri: PNode; i: int; typ: PType; result: var Rope; + argsCounter: var int) = if i < typ.len: # 'var T' is 'T&' in C++. This means we ignore the request of # any nkHiddenAddr when it's a 'var T'. let paramType = typ.n[i] assert(paramType.kind == nkSym) if paramType.typ.isCompileTimeOnly: - result = nil + discard elif typ[i].kind in {tyVar} and ri[i].kind == nkHiddenAddr: - result = genArgNoParam(p, ri[i][0]) + if argsCounter > 0: result.add ", " + genArgNoParam(p, ri[i][0], result) + inc argsCounter else: - result = genArgNoParam(p, ri[i]) #, typ.n[i].sym) + if argsCounter > 0: result.add ", " + genArgNoParam(p, ri[i], result) #, typ.n[i].sym) + inc argsCounter else: if tfVarargs notin typ.flags: localError(p.config, ri.info, "wrong argument count") - result = nil else: - result = genArgNoParam(p, ri[i]) + if argsCounter > 0: result.add ", " + genArgNoParam(p, ri[i], result) + inc argsCounter discard """ Dot call syntax in C++ @@ -565,7 +580,7 @@ proc skipAddrDeref(node: PNode): PNode = else: result = node -proc genThisArg(p: BProc; ri: PNode; i: int; typ: PType): Rope = +proc genThisArg(p: BProc; ri: PNode; i: int; typ: PType; result: var Rope) = # for better or worse c2nim translates the 'this' argument to a 'var T'. # However manual wrappers may also use 'ptr T'. In any case we support both # for convenience. @@ -579,75 +594,72 @@ proc genThisArg(p: BProc; ri: PNode; i: int; typ: PType): Rope = if t.kind in {tyVar}: let x = if ri.kind == nkHiddenAddr: ri[0] else: ri if x.typ.kind == tyPtr: - result = genArgNoParam(p, x) + genArgNoParam(p, x, result) result.add("->") elif x.kind in {nkHiddenDeref, nkDerefExpr} and x[0].typ.kind == tyPtr: - result = genArgNoParam(p, x[0]) + genArgNoParam(p, x[0], result) result.add("->") else: - result = genArgNoParam(p, x) + genArgNoParam(p, x, result) result.add(".") elif t.kind == tyPtr: if ri.kind in {nkAddr, nkHiddenAddr}: - result = genArgNoParam(p, ri[0]) + genArgNoParam(p, ri[0], result) result.add(".") else: - result = genArgNoParam(p, ri) + genArgNoParam(p, ri, result) result.add("->") else: ri = skipAddrDeref(ri) if ri.kind in {nkAddr, nkHiddenAddr}: ri = ri[0] - result = genArgNoParam(p, ri) #, typ.n[i].sym) + genArgNoParam(p, ri, result) #, typ.n[i].sym) result.add(".") -proc genPatternCall(p: BProc; ri: PNode; pat: string; typ: PType): Rope = +proc genPatternCall(p: BProc; ri: PNode; pat: string; typ: PType; result: var Rope) = var i = 0 var j = 1 while i < pat.len: case pat[i] of '@': - var first = true + var argsCounter = 0 for k in j.. 0: - if not first: - result.add(~", ") - first = false - result.add arg + genOtherArg(p, ri, k, typ, result, argsCounter) inc i of '#': if i+1 < pat.len and pat[i+1] in {'+', '@'}: let ri = ri[j] if ri.kind in nkCallKinds: let typ = skipTypes(ri[0].typ, abstractInst) - if pat[i+1] == '+': result.add genArgNoParam(p, ri[0]) - result.add(~"(") + if pat[i+1] == '+': genArgNoParam(p, ri[0], result) + result.add("(") if 1 < ri.len: - result.add genOtherArg(p, ri, 1, typ) + var argsCounterB = 0 + genOtherArg(p, ri, 1, typ, result, argsCounterB) for k in j+1.. 0 if pat.contains({'#', '(', '@', '\''}): - var pl = genPatternCall(p, ri, pat, typ) + var pl = newRopeAppender() + genPatternCall(p, ri, pat, typ, pl) # simpler version of 'fixupCall' that works with the pl+params combination: var typ = skipTypes(ri[0].typ, abstractInst) if typ[0] != nil: @@ -687,80 +700,79 @@ proc genInfixCall(p: BProc, le, ri: PNode, d: var TLoc) = list.r = pl genAssignment(p, d, list, {}) # no need for deep copying else: - pl.add(~";$n") + pl.add(";\n") line(p, cpsStmts, pl) else: - var pl: Rope = nil - #var param = typ.n[1].sym + var pl = newRopeAppender() + var argsCounter = 0 if 1 < ri.len: - pl.add(genThisArg(p, ri, 1, typ)) + genThisArg(p, ri, 1, typ, pl) pl.add(op.r) - var params: Rope + var params = newRopeAppender() for i in 2.. 0 var start = 3 if ' ' in pat: start = 1 pl.add(op.r) if ri.len > 1: - pl.add(~": ") - pl.add(genArg(p, ri[1], typ.n[1].sym, ri)) + pl.add(": ") + genArg(p, ri[1], typ.n[1].sym, ri, pl) start = 2 else: if ri.len > 1: - pl.add(genArg(p, ri[1], typ.n[1].sym, ri)) - pl.add(~" ") + genArg(p, ri[1], typ.n[1].sym, ri, pl) + pl.add(" ") pl.add(op.r) if ri.len > 2: - pl.add(~": ") - pl.add(genArg(p, ri[2], typ.n[2].sym, ri)) + pl.add(": ") + genArg(p, ri[2], typ.n[2].sym, ri, pl) for i in start..= typ.len: internalError(p.config, ri.info, "varargs for objective C method?") assert(typ.n[i].kind == nkSym) var param = typ.n[i].sym - pl.add(~" ") + pl.add(" ") pl.add(param.name.s) - pl.add(~": ") - pl.add(genArg(p, ri[i], param, ri)) + pl.add(": ") + genArg(p, ri[i], param, ri, pl) if typ[0] != nil: if isInvalidReturnType(p.config, typ): - if ri.len > 1: pl.add(~" ") + if ri.len > 1: pl.add(" ") # beware of 'result = p(result)'. We always allocate a temporary: if d.k in {locTemp, locNone}: # We already got a temp. Great, special case it: if d.k == locNone: getTemp(p, typ[0], d, needsInit=true) - pl.add(~"Result: ") + pl.add("Result: ") pl.add(addrLoc(p.config, d)) - pl.add(~"];$n") + pl.add("];\n") line(p, cpsStmts, pl) else: var tmp: TLoc getTemp(p, typ[0], tmp, needsInit=true) pl.add(addrLoc(p.config, tmp)) - pl.add(~"];$n") + pl.add("];\n") line(p, cpsStmts, pl) genAssignment(p, d, tmp, {}) # no need for deep copying else: - pl.add(~"]") + pl.add("]") if d.k == locNone: getTemp(p, typ[0], d) assert(d.t != nil) # generate an assignment to d: var list: TLoc @@ -768,7 +780,7 @@ proc genNamedParamCall(p: BProc, ri: PNode, d: var TLoc) = list.r = pl genAssignment(p, d, list, {}) # no need for deep copying else: - pl.add(~"];$n") + pl.add("];\n") line(p, cpsStmts, pl) proc notYetAlive(n: PNode): bool {.inline.} = diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index 1a3e217b2a6c..63de3ba14923 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -18,31 +18,31 @@ proc getNullValueAuxT(p: BProc; orig, t: PType; obj, constOrNil: PNode, # -------------------------- constant expressions ------------------------ -proc rdSetElemLoc(conf: ConfigRef; a: TLoc, typ: PType): Rope +proc rdSetElemLoc(conf: ConfigRef; a: TLoc, typ: PType; result: var Rope) -proc int64Literal(i: BiggestInt): Rope = +proc int64Literal(i: BiggestInt; result: var Rope) = if i > low(int64): - result = "IL64($1)" % [rope(i)] + result.add "IL64($1)" % [rope(i)] else: - result = ~"(IL64(-9223372036854775807) - IL64(1))" + result.add "(IL64(-9223372036854775807) - IL64(1))" -proc uint64Literal(i: uint64): Rope = rope($i & "ULL") +proc uint64Literal(i: uint64; result: var Rope) = result.add rope($i & "ULL") -proc intLiteral(i: BiggestInt): Rope = +proc intLiteral(i: BiggestInt; result: var Rope) = if i > low(int32) and i <= high(int32): - result = rope(i) + result.add rope(i) elif i == low(int32): # Nim has the same bug for the same reasons :-) - result = ~"(-2147483647 -1)" + result.add "(-2147483647 -1)" elif i > low(int64): - result = "IL64($1)" % [rope(i)] + result.add "IL64($1)" % [rope(i)] else: - result = ~"(IL64(-9223372036854775807) - IL64(1))" + result.add "(IL64(-9223372036854775807) - IL64(1))" -proc intLiteral(i: Int128): Rope = - intLiteral(toInt64(i)) +proc intLiteral(i: Int128; result: var Rope) = + intLiteral(toInt64(i), result) -proc genLiteral(p: BProc, n: PNode, ty: PType): Rope = +proc genLiteral(p: BProc, n: PNode, ty: PType; result: var Rope) = case n.kind of nkCharLit..nkUInt64Lit: var k: TTypeKind @@ -56,65 +56,68 @@ proc genLiteral(p: BProc, n: PNode, ty: PType): Rope = else: k = tyNil # don't go into the case variant that uses 'ty' case k of tyChar, tyNil: - result = intLiteral(n.intVal) + intLiteral(n.intVal, result) of tyBool: - if n.intVal != 0: result = ~"NIM_TRUE" - else: result = ~"NIM_FALSE" - of tyInt64: result = int64Literal(n.intVal) - of tyUInt64: result = uint64Literal(uint64(n.intVal)) - else: - result = "(($1) $2)" % [getTypeDesc(p.module, - ty), intLiteral(n.intVal)] + if n.intVal != 0: result.add "NIM_TRUE" + else: result.add "NIM_FALSE" + of tyInt64: int64Literal(n.intVal, result) + of tyUInt64: uint64Literal(uint64(n.intVal), result) + else: + result.add "((" + result.add getTypeDesc(p.module, ty) + result.add ")" + intLiteral(n.intVal, result) + result.add ")" of nkNilLit: 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) + let tmpName = p.module.tmpBase & rope(id) if id == p.module.labels: # not found in cache: inc(p.module.labels) - p.module.s[cfsData].addf( + p.module.s[cfsStrData].addf( "static NIM_CONST $1 $2 = {NIM_NIL,NIM_NIL};$n", - [getTypeDesc(p.module, ty), result]) + [getTypeDesc(p.module, ty), tmpName]) + result.add tmpName elif k in {tyPointer, tyNil, tyProc}: - result = rope("NIM_NIL") + result.add rope("NIM_NIL") else: - result = "(($1) NIM_NIL)" % [getTypeDesc(p.module, ty)] + result.add "(($1) NIM_NIL)" % [getTypeDesc(p.module, ty)] of nkStrLit..nkTripleStrLit: let k = if ty == nil: tyString else: skipTypes(ty, abstractVarRange + {tyStatic, tyUserTypeClass, tyUserTypeClassInst}).kind case k of tyNil: - result = genNilStringLiteral(p.module, n.info) + genNilStringLiteral(p.module, n.info, result) of tyString: # with the new semantics for not 'nil' strings, we can map "" to nil and # save tons of allocations: if n.strVal.len == 0 and optSeqDestructors notin p.config.globalOptions: - result = genNilStringLiteral(p.module, n.info) + genNilStringLiteral(p.module, n.info, result) else: - result = genStringLiteral(p.module, n) + genStringLiteral(p.module, n, result) else: - result = makeCString(n.strVal) + result.add makeCString(n.strVal) of nkFloatLit, nkFloat64Lit: if ty.kind == tyFloat32: - result = rope(n.floatVal.float32.toStrMaxPrecision) + result.add rope(n.floatVal.float32.toStrMaxPrecision) else: - result = rope(n.floatVal.toStrMaxPrecision) + result.add rope(n.floatVal.toStrMaxPrecision) of nkFloat32Lit: - result = rope(n.floatVal.float32.toStrMaxPrecision) + result.add rope(n.floatVal.float32.toStrMaxPrecision) else: internalError(p.config, n.info, "genLiteral(" & $n.kind & ')') - result = nil -proc genLiteral(p: BProc, n: PNode): Rope = - result = genLiteral(p, n, n.typ) +proc genLiteral(p: BProc, n: PNode; result: var Rope) = + genLiteral(p, n, n.typ, result) proc bitSetToWord(s: TBitSet, size: int): BiggestUInt = result = 0 for j in 0.. 8: var res = "{\n" for i in 0.. 8: let id = nodeTableTestOrSet(p.module.dataCache, n, p.module.labels) - result = p.module.tmpBase & rope(id) + let tmpName = p.module.tmpBase & rope(id) if id == p.module.labels: # not found in cache: inc(p.module.labels) - p.module.s[cfsData].addf("static NIM_CONST $1 $2 = $3;$n", - [getTypeDesc(p.module, n.typ), result, genRawSetData(cs, size)]) + p.module.s[cfsStrData].addf("static NIM_CONST $1 $2 = ", + [getTypeDesc(p.module, n.typ), tmpName]) + genRawSetData(cs, size, p.module.s[cfsStrData]) + p.module.s[cfsStrData].addf(";$n", []) + result.add tmpName else: - result = genRawSetData(cs, size) + genRawSetData(cs, size, result) proc getStorageLoc(n: PNode): TStorageLoc = ## deadcode @@ -214,7 +220,7 @@ proc asgnComplexity(n: PNode): int = else: discard proc optAsgnLoc(a: TLoc, t: PType, field: Rope): TLoc = - assert field != nil + assert field != "" result.k = locField result.storage = a.storage result.lode = lodeTyp t @@ -248,7 +254,7 @@ proc genOptAsgnObject(p: BProc, dest, src: TLoc, flags: TAssignmentFlags, case t.kind of nkSym: let field = t.sym - if field.loc.r == nil: fillObjectFields(p.module, typ) + if field.loc.r == "": fillObjectFields(p.module, typ) genAssignment(p, optAsgnLoc(dest, field.typ, field.loc.r), optAsgnLoc(src, field.typ, field.loc.r), newflags) of nkRecList: @@ -549,12 +555,21 @@ template binaryArithOverflowRaw(p: BProc, t: PType, a, b: TLoc; else: getTypeDesc(p.module, t) var result = getTempName(p.module) linefmt(p, cpsLocals, "$1 $2;$n", [storage, result]) - lineCg(p, cpsStmts, "if (#$2($3, $4, &$1)) { #raiseOverflow(); $5};$n", - [result, cpname, rdCharLoc(a), rdCharLoc(b), raiseInstr(p)]) + lineCg(p, cpsStmts, "if (#$2($3, $4, &$1)) { #raiseOverflow(); ", + [result, cpname, rdCharLoc(a), rdCharLoc(b)]) + raiseInstr(p, p.s(cpsStmts)) + linefmt p, cpsStmts, "};$n", [] + if size < p.config.target.intSize or t.kind in {tyRange, tyEnum}: - linefmt(p, cpsStmts, "if ($1 < $2 || $1 > $3){ #raiseOverflow(); $4}$n", - [result, intLiteral(firstOrd(p.config, t)), intLiteral(lastOrd(p.config, t)), - raiseInstr(p)]) + var first = newRopeAppender() + intLiteral(firstOrd(p.config, t), first) + var last = newRopeAppender() + intLiteral(lastOrd(p.config, t), last) + linefmt(p, cpsStmts, "if ($1 < $2 || $1 > $3){ #raiseOverflow(); ", + [result, first, last]) + raiseInstr(p, p.s(cpsStmts)) + linefmt p, cpsStmts, "}$n", [] + result proc binaryArithOverflow(p: BProc, e: PNode, d: var TLoc, m: TMagic) = @@ -592,8 +607,9 @@ proc binaryArithOverflow(p: BProc, e: PNode, d: var TLoc, m: TMagic) = if e[2].kind in {nkIntLit..nkInt64Lit}: needsOverflowCheck = e[2].intVal == -1 if canBeZero: - linefmt(p, cpsStmts, "if ($1 == 0){ #raiseDivByZero(); $2}$n", - [rdLoc(b), raiseInstr(p)]) + linefmt(p, cpsStmts, "if ($1 == 0){ #raiseDivByZero(); ", [rdLoc(b)]) + raiseInstr(p, p.s(cpsStmts)) + linefmt(p, cpsStmts, "}$n", []) if needsOverflowCheck: let res = binaryArithOverflowRaw(p, t, a, b, if t.kind == tyInt64: prc64[m] else: prc[m]) @@ -610,8 +626,13 @@ proc unaryArithOverflow(p: BProc, e: PNode, d: var TLoc, m: TMagic) = initLocExpr(p, e[1], a) t = skipTypes(e.typ, abstractRange) if optOverflowCheck in p.options: - linefmt(p, cpsStmts, "if ($1 == $2){ #raiseOverflow(); $3}$n", - [rdLoc(a), intLiteral(firstOrd(p.config, t)), raiseInstr(p)]) + var first = newRopeAppender() + intLiteral(firstOrd(p.config, t), first) + linefmt(p, cpsStmts, "if ($1 == $2){ #raiseOverflow(); ", + [rdLoc(a), first]) + raiseInstr(p, p.s(cpsStmts)) + linefmt p, cpsStmts, "}$n", [] + case m of mUnaryMinusI: putIntoDest(p, d, e, "((NI$2)-($1))" % [rdLoc(a), rope(getSize(p.config, t) * 8)]) @@ -731,7 +752,22 @@ proc isCppRef(p: BProc; typ: PType): bool {.inline.} = skipTypes(typ, abstractInstOwned).kind in {tyVar} and tfVarIsPtr notin skipTypes(typ, abstractInstOwned).flags +proc derefBlock(p: BProc, e: PNode, d: var TLoc) = + # We transform (block: x)[] to (block: x[]) + let e0 = e[0] + var n = shallowCopy(e0) + n.typ = e.typ + for i in 0 ..< e0.len - 1: + n[i] = e0[i] + n[e0.len-1] = newTreeIT(nkHiddenDeref, e.info, e.typ, e0[e0.len-1]) + expr p, n, d + proc genDeref(p: BProc, e: PNode, d: var TLoc) = + if e.kind == nkHiddenDeref and e[0].kind in {nkBlockExpr, nkBlockStmt}: + # bug #20107. Watch out to not deref the pointer too late. + derefBlock(p, e, d) + return + let mt = mapType(p.config, e[0].typ, mapTypeChooser(e[0])) if mt in {ctArray, ctPtrToArray} and lfEnforceDeref notin d.flags: # XXX the amount of hacks for C's arrays is incredible, maybe we should @@ -835,7 +871,7 @@ proc genTupleElem(p: BProc, e: PNode, d: var TLoc) = proc lookupFieldAgain(p: BProc, ty: PType; field: PSym; r: var Rope; resTyp: ptr PType = nil): PSym = var ty = ty - assert r != nil + assert r != "" while ty != nil: ty = ty.skipTypes(skipPtrs) assert(ty.kind in {tyTuple, tyObject}) @@ -856,15 +892,18 @@ proc genRecordField(p: BProc, e: PNode, d: var TLoc) = if ty.kind == tyTuple: # we found a unique tuple type which lacks field information # so we use Field$i - r.addf(".Field$1", [rope(f.position)]) + r.add ".Field" + r.add rope(f.position) putIntoDest(p, d, e, r, a.storage) else: var rtyp: PType let field = lookupFieldAgain(p, ty, f, r, addr rtyp) - if field.loc.r == nil and rtyp != nil: fillObjectFields(p.module, rtyp) - if field.loc.r == nil: internalError(p.config, e.info, "genRecordField 3 " & typeToString(ty)) - r.addf(".$1", [field.loc.r]) + if field.loc.r == "" and rtyp != nil: fillObjectFields(p.module, rtyp) + if field.loc.r == "": internalError(p.config, e.info, "genRecordField 3 " & typeToString(ty)) + r.add "." + r.add field.loc.r putIntoDest(p, d, e, r, a.storage) + r.freeze proc genInExprAux(p: BProc, e: PNode, a, b, d: var TLoc) @@ -881,7 +920,8 @@ proc genFieldCheck(p: BProc, e: PNode, obj: Rope, field: PSym) = initLoc(test, locNone, it, OnStack) initLocExpr(p, it[1], u) initLoc(v, locExpr, disc, OnUnknown) - v.r = obj + v.r = newRopeAppender() + v.r.add obj v.r.add(".") v.r.add(disc.sym.loc.r) genInExprAux(p, it, u, v, test) @@ -894,33 +934,40 @@ proc genFieldCheck(p: BProc, e: PNode, obj: Rope, field: PSym) = # passing around `TLineInfo` + the set of files in the project. msg.add toFileLineCol(p.config, e.info) & " " msg.add genFieldDefect(p.config, field.name.s, disc.sym) - let strLit = genStringLiteral(p.module, newStrNode(nkStrLit, msg)) + var strLit = newRopeAppender() + genStringLiteral(p.module, newStrNode(nkStrLit, msg), strLit) ## discriminant check template fun(code) = linefmt(p, cpsStmts, code, [rdLoc(test)]) if op.magic == mNot: fun("if ($1) ") else: fun("if (!($1)) ") ## call raiseFieldError2 on failure - let discIndex = rdSetElemLoc(p.config, v, u.t) + var discIndex = newRopeAppender() + rdSetElemLoc(p.config, v, u.t, discIndex) if optTinyRtti in p.config.globalOptions: # not sure how to use `genEnumToStr` here - if p.config.getStdlibVersion < (1,5,1): - const code = "{ #raiseFieldError($1); $2} $n" - linefmt(p, cpsStmts, code, [strLit, raiseInstr(p)]) + if p.config.getStdlibVersion < (1, 5, 1): + const code = "{ #raiseFieldError($1); " + linefmt(p, cpsStmts, code, [strLit]) else: - const code = "{ #raiseFieldError2($1, (NI)$3); $2} $n" - linefmt(p, cpsStmts, code, [strLit, raiseInstr(p), discIndex]) + const code = "{ #raiseFieldError2($1, (NI)$2); " + linefmt(p, cpsStmts, code, [strLit, discIndex]) + else: # complication needed for signed types let first = p.config.firstOrd(disc.sym.typ) - let firstLit = int64Literal(cast[int](first)) + var firstLit = newRopeAppender() + int64Literal(cast[int](first), firstLit) let discName = genTypeInfo(p.config, p.module, disc.sym.typ, e.info) if p.config.getStdlibVersion < (1,5,1): - const code = "{ #raiseFieldError($1); $2} $n" - linefmt(p, cpsStmts, code, [strLit, raiseInstr(p)]) + const code = "{ #raiseFieldError($1); " + linefmt(p, cpsStmts, code, [strLit]) else: - const code = "{ #raiseFieldError2($1, #reprDiscriminant(((NI)$3) + (NI)$4, $5)); $2} $n" - linefmt(p, cpsStmts, code, [strLit, raiseInstr(p), discIndex, firstLit, discName]) + const code = "{ #raiseFieldError2($1, #reprDiscriminant(((NI)$2) + (NI)$3, $4)); " + linefmt(p, cpsStmts, code, [strLit, discIndex, firstLit, discName]) + + raiseInstr(p, p.s(cpsStmts)) + linefmt p, cpsStmts, "}$n", [] proc genCheckedRecordField(p: BProc, e: PNode, d: var TLoc) = assert e[0].kind == nkDotExpr @@ -931,12 +978,14 @@ proc genCheckedRecordField(p: BProc, e: PNode, d: var TLoc) = var r = rdLoc(a) let f = e[0][1].sym let field = lookupFieldAgain(p, ty, f, r) - if field.loc.r == nil: fillObjectFields(p.module, ty) - if field.loc.r == nil: + if field.loc.r == "": fillObjectFields(p.module, ty) + if field.loc.r == "": internalError(p.config, e.info, "genCheckedRecordField") # generate the checks: genFieldCheck(p, e, r, field) - r.add(ropecg(p.module, ".$1", [field.loc.r])) + r.add(".") + r.add field.loc.r putIntoDest(p, d, e[0], r, a.storage) + r.freeze else: genRecordField(p, e[0], d) @@ -953,18 +1002,28 @@ proc genArrayElem(p: BProc, n, x, y: PNode, d: var TLoc) = initLocExpr(p, x, a) initLocExpr(p, y, b) var ty = skipTypes(a.t, abstractVarRange + abstractPtrs + tyUserTypeClasses) - var first = intLiteral(firstOrd(p.config, ty)) + var first = newRopeAppender() + intLiteral(firstOrd(p.config, ty), first) # emit range check: if optBoundsCheck in p.options and ty.kind != tyUncheckedArray: if not isConstExpr(y): # semantic pass has already checked for const index expressions if firstOrd(p.config, ty) == 0 and lastOrd(p.config, ty) >= 0: if (firstOrd(p.config, b.t) < firstOrd(p.config, ty)) or (lastOrd(p.config, b.t) > lastOrd(p.config, ty)): - linefmt(p, cpsStmts, "if ((NU)($1) > (NU)($2)){ #raiseIndexError2($1, $2); $3}$n", - [rdCharLoc(b), intLiteral(lastOrd(p.config, ty)), raiseInstr(p)]) + var last = newRopeAppender() + intLiteral(lastOrd(p.config, ty), last) + linefmt(p, cpsStmts, "if ((NU)($1) > (NU)($2)){ #raiseIndexError2($1, $2); ", + [rdCharLoc(b), last]) + raiseInstr(p, p.s(cpsStmts)) + linefmt p, cpsStmts, "}$n", [] else: - linefmt(p, cpsStmts, "if ($1 < $2 || $1 > $3){ #raiseIndexError3($1, $2, $3); $4}$n", - [rdCharLoc(b), first, intLiteral(lastOrd(p.config, ty)), raiseInstr(p)]) + var last = newRopeAppender() + intLiteral(lastOrd(p.config, ty), last) + linefmt(p, cpsStmts, "if ($1 < $2 || $1 > $3){ #raiseIndexError3($1, $2, $3); ", + [rdCharLoc(b), first, last]) + raiseInstr(p, p.s(cpsStmts)) + linefmt p, cpsStmts, "}$n", [] + else: let idx = getOrdValue(y) if idx < firstOrd(p.config, ty) or idx > lastOrd(p.config, ty): @@ -988,24 +1047,37 @@ proc genBoundsCheck(p: BProc; arr, a, b: TLoc) = if reifiedOpenArray(arr.lode): linefmt(p, cpsStmts, "if ($2-$1 != -1 && " & - "((NU)($1) >= (NU)($3.Field1) || (NU)($2) >= (NU)($3.Field1))){ #raiseIndexError(); $4}$n", - [rdLoc(a), rdLoc(b), rdLoc(arr), raiseInstr(p)]) + "($1 < 0 || $1 >= $3.Field1 || $2 < 0 || $2 >= $3.Field1)){ #raiseIndexError4($1, $2, $3.Field1); ", + [rdLoc(a), rdLoc(b), rdLoc(arr)]) else: linefmt(p, cpsStmts, - "if ($2-$1 != -1 && " & - "((NU)($1) >= (NU)($3Len_0) || (NU)($2) >= (NU)($3Len_0))){ #raiseIndexError(); $4}$n", - [rdLoc(a), rdLoc(b), rdLoc(arr), raiseInstr(p)]) + "if ($2-$1 != -1 && ($1 < 0 || $1 >= $3Len_0 || $2 < 0 || $2 >= $3Len_0))" & + "{ #raiseIndexError4($1, $2, $3Len_0); ", + [rdLoc(a), rdLoc(b), rdLoc(arr)]) + raiseInstr(p, p.s(cpsStmts)) + linefmt p, cpsStmts, "}$n", [] + of tyArray: - let first = intLiteral(firstOrd(p.config, ty)) + var first = newRopeAppender() + intLiteral(firstOrd(p.config, ty), first) + var last = newRopeAppender() + intLiteral(lastOrd(p.config, ty), last) linefmt(p, cpsStmts, "if ($2-$1 != -1 && " & - "($2-$1 < -1 || $1 < $3 || $1 > $4 || $2 < $3 || $2 > $4)){ #raiseIndexError(); $5}$n", - [rdCharLoc(a), rdCharLoc(b), first, intLiteral(lastOrd(p.config, ty)), raiseInstr(p)]) + "($2-$1 < -1 || $1 < $3 || $1 > $4 || $2 < $3 || $2 > $4)){ #raiseIndexError(); ", + [rdCharLoc(a), rdCharLoc(b), first, last]) + + raiseInstr(p, p.s(cpsStmts)) + linefmt p, cpsStmts, "}$n", [] + of tySequence, tyString: linefmt(p, cpsStmts, "if ($2-$1 != -1 && " & - "((NU)($1) >= (NU)$3 || (NU)($2) >= (NU)$3)){ #raiseIndexError(); $4}$n", - [rdLoc(a), rdLoc(b), lenExpr(p, arr), raiseInstr(p)]) + "($1 < 0 || $1 >= $3 || $2 < 0 || $2 >= $3)){ #raiseIndexError4($1, $2, $3); ", + [rdLoc(a), rdLoc(b), lenExpr(p, arr)]) + raiseInstr(p, p.s(cpsStmts)) + linefmt p, cpsStmts, "}$n", [] + else: discard proc genOpenArrayElem(p: BProc, n, x, y: PNode, d: var TLoc) = @@ -1015,15 +1087,21 @@ proc genOpenArrayElem(p: BProc, n, x, y: PNode, d: var TLoc) = if not reifiedOpenArray(x): # emit range check: if optBoundsCheck in p.options: - linefmt(p, cpsStmts, "if ((NU)($1) >= (NU)($2Len_0)){ #raiseIndexError2($1,$2Len_0-1); $3}$n", - [rdCharLoc(b), rdLoc(a), raiseInstr(p)]) # BUGFIX: ``>=`` and not ``>``! + linefmt(p, cpsStmts, "if ($1 < 0 || $1 >= $2Len_0){ #raiseIndexError2($1,$2Len_0-1); ", + [rdCharLoc(b), rdLoc(a)]) # BUGFIX: ``>=`` and not ``>``! + raiseInstr(p, p.s(cpsStmts)) + linefmt p, cpsStmts, "}$n", [] + inheritLocation(d, a) putIntoDest(p, d, n, ropecg(p.module, "$1[$2]", [rdLoc(a), rdCharLoc(b)]), a.storage) else: if optBoundsCheck in p.options: - linefmt(p, cpsStmts, "if ((NU)($1) >= (NU)($2.Field1)){ #raiseIndexError2($1,$2.Field1-1); $3}$n", - [rdCharLoc(b), rdLoc(a), raiseInstr(p)]) # BUGFIX: ``>=`` and not ``>``! + linefmt(p, cpsStmts, "if ($1 < 0 || $1 >= $2.Field1){ #raiseIndexError2($1,$2.Field1-1); ", + [rdCharLoc(b), rdLoc(a)]) # BUGFIX: ``>=`` and not ``>``! + raiseInstr(p, p.s(cpsStmts)) + linefmt p, cpsStmts, "}$n", [] + inheritLocation(d, a) putIntoDest(p, d, n, ropecg(p.module, "$1.Field0[$2]", [rdLoc(a), rdCharLoc(b)]), a.storage) @@ -1037,8 +1115,11 @@ proc genSeqElem(p: BProc, n, x, y: PNode, d: var TLoc) = ty = skipTypes(ty.lastSon, abstractVarRange) # emit range check: if optBoundsCheck in p.options: linefmt(p, cpsStmts, - "if ((NU)($1) >= (NU)$2){ #raiseIndexError2($1,$2-1); $3}$n", - [rdCharLoc(b), lenExpr(p, a), raiseInstr(p)]) + "if ($1 < 0 || $1 >= $2){ #raiseIndexError2($1,$2-1); ", + [rdCharLoc(b), lenExpr(p, a)]) + raiseInstr(p, p.s(cpsStmts)) + linefmt p, cpsStmts, "}$n", [] + if d.k == locNone: d.storage = OnHeap if skipTypes(a.t, abstractVar).kind in {tyRef, tyPtr}: a.r = ropecg(p.module, "(*$1)", [a.r]) @@ -1142,7 +1223,7 @@ proc genEcho(p: BProc, n: PNode) = internalAssert p.config, n.kind == nkBracket if p.config.target.targetOS == osGenode: # echo directly to the Genode LOG session - var args: Rope = nil + var args: Rope = "" var a: TLoc for i, it in n.sons: if it.skipConv.kind == nkNilLit: @@ -1183,7 +1264,7 @@ proc strLoc(p: BProc; d: TLoc): Rope = proc genStrConcat(p: BProc, e: PNode, d: var TLoc) = # - # s = 'Hello ' & name & ', how do you feel?' & 'z' + # s = "Hello " & name & ", how do you feel?" & 'z' # # # { @@ -1201,8 +1282,8 @@ proc genStrConcat(p: BProc, e: PNode, d: var TLoc) = var a, tmp: TLoc getTemp(p, e.typ, tmp) var L = 0 - var appends: Rope = nil - var lens: Rope = nil + var appends: Rope = "" + var lens: Rope = "" for i in 0.. - # s &= 'Hello ' & name & ', how do you feel?' & 'z' + # s &= "Hello " & name & ", how do you feel?" & 'z' # // BUG: what if s is on the left side too? # # { @@ -1319,7 +1400,7 @@ proc rawGenNew(p: BProc, a: var TLoc, sizeExpr: Rope; needsInit: bool) = let refType = typ.skipTypes(abstractInstOwned) assert refType.kind == tyRef let bt = refType.lastSon - if sizeExpr.isNil: + if sizeExpr == "": sizeExpr = "sizeof($1)" % [getTypeDesc(p.module, bt)] if optTinyRtti in p.config.globalOptions: @@ -1375,7 +1456,7 @@ proc genNew(p: BProc, e: PNode) = initLocExpr(p, e[2], se) rawGenNew(p, a, se.rdLoc, needsInit = true) else: - rawGenNew(p, a, nil, needsInit = true) + rawGenNew(p, a, "", needsInit = true) gcUsage(p.config, e) proc genNewSeqAux(p: BProc, dest: TLoc, length: Rope; lenIsZero: bool) = @@ -1445,8 +1526,9 @@ proc rawConstExpr(p: BProc, n: PNode; d: var TLoc) = if id == p.module.labels: # expression not found in the cache: inc(p.module.labels) - p.module.s[cfsData].addf("static NIM_CONST $1 $2 = $3;$n", - [getTypeDesc(p.module, t), d.r, genBracedInit(p, n, isConst = true, t)]) + p.module.s[cfsData].addf("static NIM_CONST $1 $2 = ", [getTypeDesc(p.module, t), d.r]) + genBracedInit(p, n, isConst = true, t, p.module.s[cfsData]) + p.module.s[cfsData].addf(";$n", []) proc handleConstExpr(p: BProc, n: PNode, d: var TLoc): bool = if d.k == locNone and n.len > ord(n.kind == nkObjConstr) and n.isDeepConstExpr: @@ -1483,7 +1565,7 @@ proc genObjConstr(p: BProc, e: PNode, d: var TLoc) = getTemp(p, t, tmp) r = rdLoc(tmp) if isRef: - rawGenNew(p, tmp, nil, needsInit = nfAllFieldsSet notin e.flags) + rawGenNew(p, tmp, "", needsInit = nfAllFieldsSet notin e.flags) t = t.lastSon.skipTypes(abstractInstOwned) r = "(*$1)" % [r] gcUsage(p.config, e) @@ -1499,8 +1581,8 @@ proc genObjConstr(p: BProc, e: PNode, d: var TLoc) = var tmp2: TLoc tmp2.r = r let field = lookupFieldAgain(p, ty, it[0].sym, tmp2.r) - if field.loc.r == nil: fillObjectFields(p.module, ty) - if field.loc.r == nil: internalError(p.config, e.info, "genObjConstr") + if field.loc.r == "": fillObjectFields(p.module, ty) + if field.loc.r == "": internalError(p.config, e.info, "genObjConstr") if it.len == 3 and optFieldCheck in p.options: genFieldCheck(p, it[2], r, field) tmp2.r.add(".") @@ -1533,18 +1615,21 @@ proc genSeqConstr(p: BProc, n: PNode, d: var TLoc) = elif d.k == locNone: getTemp(p, n.typ, d) - let l = intLiteral(n.len) + var lit = newRopeAppender() + intLiteral(n.len, lit) if optSeqDestructors in p.config.globalOptions: let seqtype = n.typ linefmt(p, cpsStmts, "$1.len = $2; $1.p = ($4*) #newSeqPayload($2, sizeof($3), NIM_ALIGNOF($3));$n", - [rdLoc dest[], l, getTypeDesc(p.module, seqtype.lastSon), + [rdLoc dest[], lit, getTypeDesc(p.module, seqtype.lastSon), getSeqPayloadType(p.module, seqtype)]) else: # generate call to newSeq before adding the elements per hand: - genNewSeqAux(p, dest[], l, n.len == 0) + genNewSeqAux(p, dest[], lit, n.len == 0) for i in 0..typeInfoV1" proc genGetTypeInfo(p: BProc, e: PNode, d: var TLoc) = - discard cgsym(p.module, "TNimType") + cgsym(p.module, "TNimType") let t = e[1].typ # ordinary static type information putIntoDest(p, d, e, genTypeInfoV1(p.module, t, e.info)) @@ -1747,16 +1851,20 @@ proc genGetTypeInfoV2(p: BProc, e: PNode, d: var TLoc) = else: var a: TLoc initLocExpr(p, e[1], a) - var nilCheck = Rope(nil) + var nilCheck = "" # use the dynamic type stored at offset 0: - putIntoDest(p, d, e, rdMType(p, a, nilCheck)) + var rt = newRopeAppender() + rdMType(p, a, nilCheck, rt) + putIntoDest(p, d, e, rt) proc genAccessTypeField(p: BProc; e: PNode; d: var TLoc) = var a: TLoc initLocExpr(p, e[1], a) - var nilCheck = Rope(nil) + var nilCheck = "" # use the dynamic type stored at offset 0: - putIntoDest(p, d, e, rdMType(p, a, nilCheck)) + var rt = newRopeAppender() + rdMType(p, a, nilCheck, rt) + putIntoDest(p, d, e, rt) template genDollar(p: BProc, n: PNode, d: var TLoc, frmt: string) = var a: TLoc @@ -1884,14 +1992,17 @@ proc genSwap(p: BProc, e: PNode, d: var TLoc) = genAssignment(p, a, b, {}) genAssignment(p, b, tmp, {}) -proc rdSetElemLoc(conf: ConfigRef; a: TLoc, typ: PType): Rope = +proc rdSetElemLoc(conf: ConfigRef; a: TLoc, typ: PType; result: var Rope) = # read a location of an set element; it may need a subtraction operation # before the set operation - result = rdCharLoc(a) + result.add "(" + result.add rdCharLoc(a) let setType = typ.skipTypes(abstractPtrs) assert(setType.kind == tySet) if firstOrd(conf, setType) != 0: - result = "($1- $2)" % [result, rope(firstOrd(conf, setType))] + result.add " - " + result.add rope(firstOrd(conf, setType)) + result.add ")" proc fewCmps(conf: ConfigRef; s: PNode): bool = # this function estimates whether it is better to emit code @@ -1905,7 +2016,9 @@ proc fewCmps(conf: ConfigRef; s: PNode): bool = result = s.len <= 8 # 8 seems to be a good value template binaryExprIn(p: BProc, e: PNode, a, b, d: var TLoc, frmt: string) = - putIntoDest(p, d, e, frmt % [rdLoc(a), rdSetElemLoc(p.config, b, a.t)]) + var elem = newRopeAppender() + rdSetElemLoc(p.config, b, a.t, elem) + putIntoDest(p, d, e, frmt % [rdLoc(a), elem]) proc genInExprAux(p: BProc, e: PNode, a, b, d: var TLoc) = case int(getSize(p.config, skipTypes(e[1].typ, abstractVar))) @@ -1920,7 +2033,9 @@ template binaryStmtInExcl(p: BProc, e: PNode, d: var TLoc, frmt: string) = assert(d.k == locNone) initLocExpr(p, e[1], a) initLocExpr(p, e[2], b) - lineF(p, cpsStmts, frmt, [rdLoc(a), rdSetElemLoc(p.config, b, a.t)]) + var elem = newRopeAppender() + rdSetElemLoc(p.config, b, a.t, elem) + lineF(p, cpsStmts, frmt, [rdLoc(a), elem]) proc genInOp(p: BProc, e: PNode, d: var TLoc) = var a, b, x, y: TLoc @@ -2074,6 +2189,11 @@ proc genSomeCast(p: BProc, e: PNode, d: var TLoc) = elif etyp.kind == tyBool and srcTyp.kind in IntegralTypes: putIntoDest(p, d, e, "(($1) != 0)" % [rdCharLoc(a)], a.storage) else: + if etyp.kind == tyPtr: + # generates the definition of structs for casts like cast[ptr object](addr x)[] + let internalType = etyp.skipTypes({tyPtr}) + if internalType.kind == tyObject: + discard getTypeDesc(p.module, internalType) putIntoDest(p, d, e, "(($1) ($2))" % [getTypeDesc(p.module, e.typ), rdCharLoc(a)], a.storage) @@ -2113,16 +2233,23 @@ proc genRangeChck(p: BProc, n: PNode, d: var TLoc) = # emit range check: if n0t.kind in {tyUInt, tyUInt64}: - linefmt(p, cpsStmts, "if ($1 > ($6)($3)){ #raiseRangeErrorNoArgs(); $5}$n", - [rdCharLoc(a), genLiteral(p, n[1], dest), genLiteral(p, n[2], dest), - raiser, raiseInstr(p), getTypeDesc(p.module, n0t)]) + var first = newRopeAppender() + genLiteral(p, n[1], dest, first) + var last = newRopeAppender() + genLiteral(p, n[2], dest, last) + linefmt(p, cpsStmts, "if ($1 > ($5)($3)){ #raiseRangeErrorNoArgs(); ", + [rdCharLoc(a), first, last, + raiser, getTypeDesc(p.module, n0t)]) + raiseInstr(p, p.s(cpsStmts)) + linefmt p, cpsStmts, "}$n", [] + else: let raiser = case skipTypes(n.typ, abstractVarRange).kind of tyUInt..tyUInt64, tyChar: "raiseRangeErrorU" of tyFloat..tyFloat128: "raiseRangeErrorF" else: "raiseRangeErrorI" - discard cgsym(p.module, raiser) + cgsym(p.module, raiser) let boundaryCast = if n0t.skipTypes(abstractVarRange).kind in {tyUInt, tyUInt32, tyUInt64} or @@ -2130,9 +2257,16 @@ proc genRangeChck(p: BProc, n: PNode, d: var TLoc) = "(NI64)" else: "" - linefmt(p, cpsStmts, "if ($6($1) < $2 || $6($1) > $3){ $4($1, $2, $3); $5}$n", - [rdCharLoc(a), genLiteral(p, n[1], dest), genLiteral(p, n[2], dest), - raiser, raiseInstr(p), boundaryCast]) + var first = newRopeAppender() + genLiteral(p, n[1], dest, first) + var last = newRopeAppender() + genLiteral(p, n[2], dest, last) + linefmt(p, cpsStmts, "if ($5($1) < $2 || $5($1) > $3){ $4($1, $2, $3); ", + [rdCharLoc(a), first, last, + raiser, boundaryCast]) + raiseInstr(p, p.s(cpsStmts)) + linefmt p, cpsStmts, "}$n", [] + putIntoDest(p, d, n, "(($1) ($2))" % [getTypeDesc(p.module, dest), rdCharLoc(a)], a.storage) @@ -2186,9 +2320,15 @@ proc binaryFloatArith(p: BProc, e: PNode, d: var TLoc, m: TMagic) = [opr[m], rdLoc(a), rdLoc(b), getSimpleTypeDesc(p.module, e[1].typ)])) if optNaNCheck in p.options: - linefmt(p, cpsStmts, "if ($1 != $1){ #raiseFloatInvalidOp(); $2}$n", [rdLoc(d), raiseInstr(p)]) + linefmt(p, cpsStmts, "if ($1 != $1){ #raiseFloatInvalidOp(); ", [rdLoc(d)]) + raiseInstr(p, p.s(cpsStmts)) + linefmt p, cpsStmts, "}$n", [] + if optInfCheck in p.options: - linefmt(p, cpsStmts, "if ($1 != 0.0 && $1*0.5 == $1) { #raiseFloatOverflow($1); $2}$n", [rdLoc(d), raiseInstr(p)]) + linefmt(p, cpsStmts, "if ($1 != 0.0 && $1*0.5 == $1) { #raiseFloatOverflow($1); ", [rdLoc(d)]) + raiseInstr(p, p.s(cpsStmts)) + linefmt p, cpsStmts, "}$n", [] + else: binaryArith(p, e, d, m) @@ -2352,7 +2492,7 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) = genDollar(p, e, d, "#nimFloatToStr($1)") of mCStrToStr: genDollar(p, e, d, "#cstrToNimstr($1)") of mStrToStr, mUnown: expr(p, e[1], d) - of mIsolate, mFinished: genCall(p, e, d) + of generatedMagics: genCall(p, e, d) of mEnumToStr: if optTinyRtti in p.config.globalOptions: genEnumToStr(p, e, d) @@ -2364,7 +2504,7 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) = if optTinyRtti in p.config.globalOptions: var a: TLoc initLocExpr(p, e[1], a) - rawGenNew(p, a, nil, needsInit = true) + rawGenNew(p, a, "", needsInit = true) gcUsage(p.config, e) else: genNewFinalize(p, e) @@ -2427,7 +2567,7 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) = let wasDeclared = containsOrIncl(p.module.declaredProtos, prc.id) # Make the function behind the magic get actually generated - this will # not lead to a forward declaration! The genCall will lead to one. - discard cgsym(p.module, $opr.loc.r) + cgsym(p.module, $opr.loc.r) # make sure we have pointer-initialising code for hot code reloading if not wasDeclared and p.hcrOn: p.module.s[cfsDynLibInit].addf("\t$1 = ($2) hcrGetProc($3, \"$1\");$n", @@ -2482,7 +2622,9 @@ proc genSetConstr(p: BProc, e: PNode, d: var TLoc) = var a, b, idx: TLoc if nfAllConst in e.flags: - putIntoDest(p, d, e, genSetNode(p, e)) + var elem = newRopeAppender() + genSetNode(p, e, elem) + putIntoDest(p, d, e, elem) else: if d.k == locNone: getTemp(p, e.typ, d) if getSize(p.config, e.typ) > 8: @@ -2494,13 +2636,19 @@ proc genSetConstr(p: BProc, e: PNode, d: var TLoc) = getTemp(p, getSysType(p.module.g.graph, unknownLineInfo, tyInt), idx) # our counter initLocExpr(p, it[0], a) initLocExpr(p, it[1], b) + var aa = newRopeAppender() + rdSetElemLoc(p.config, a, e.typ, aa) + var bb = newRopeAppender() + rdSetElemLoc(p.config, b, e.typ, bb) lineF(p, cpsStmts, "for ($1 = $3; $1 <= $4; $1++) $n" & "$2[(NU)($1)>>3] |=(1U<<((NU)($1)&7U));$n", [rdLoc(idx), rdLoc(d), - rdSetElemLoc(p.config, a, e.typ), rdSetElemLoc(p.config, b, e.typ)]) + aa, bb]) else: initLocExpr(p, it, a) + var aa = newRopeAppender() + rdSetElemLoc(p.config, a, e.typ, aa) lineF(p, cpsStmts, "$1[(NU)($2)>>3] |=(1U<<((NU)($2)&7U));$n", - [rdLoc(d), rdSetElemLoc(p.config, a, e.typ)]) + [rdLoc(d), aa]) else: # small set var ts = "NU" & $(getSize(p.config, e.typ) * 8) @@ -2510,15 +2658,21 @@ proc genSetConstr(p: BProc, e: PNode, d: var TLoc) = getTemp(p, getSysType(p.module.g.graph, unknownLineInfo, tyInt), idx) # our counter initLocExpr(p, it[0], a) initLocExpr(p, it[1], b) + var aa = newRopeAppender() + rdSetElemLoc(p.config, a, e.typ, aa) + var bb = newRopeAppender() + rdSetElemLoc(p.config, b, e.typ, bb) + lineF(p, cpsStmts, "for ($1 = $3; $1 <= $4; $1++) $n" & "$2 |=(($5)(1)<<(($1)%(sizeof($5)*8)));$n", [ - rdLoc(idx), rdLoc(d), rdSetElemLoc(p.config, a, e.typ), - rdSetElemLoc(p.config, b, e.typ), rope(ts)]) + rdLoc(idx), rdLoc(d), aa, bb, rope(ts)]) else: initLocExpr(p, it, a) + var aa = newRopeAppender() + rdSetElemLoc(p.config, a, e.typ, aa) lineF(p, cpsStmts, "$1 |=(($3)(1)<<(($2)%(sizeof($3)*8)));$n", - [rdLoc(d), rdSetElemLoc(p.config, a, e.typ), rope(ts)]) + [rdLoc(d), aa, rope(ts)]) proc genTupleConstr(p: BProc, n: PNode, d: var TLoc) = var rec: TLoc @@ -2544,8 +2698,10 @@ proc genClosure(p: BProc, n: PNode, d: var TLoc) = if isConstClosure(n): inc(p.module.labels) var tmp = "CNSTCLOSURE" & rope(p.module.labels) - p.module.s[cfsData].addf("static NIM_CONST $1 $2 = $3;$n", - [getTypeDesc(p.module, n.typ), tmp, genBracedInit(p, n, isConst = true, n.typ)]) + var data = "static NIM_CONST $1 $2 = " % [getTypeDesc(p.module, n.typ), tmp] + genBracedInit(p, n, isConst = true, n.typ, data) + data.addf(";$n", []) + p.module.s[cfsData].add data putIntoDest(p, d, n, tmp, OnStatic) else: var tmp, a, b: TLoc @@ -2570,12 +2726,14 @@ proc genArrayConstr(p: BProc, n: PNode, d: var TLoc) = if d.k == locNone: getTemp(p, n.typ, d) for i in 0.. 0: exprOrStmt - if frameName != nil: + if frameName != "": p.s(cpsStmts).add deinitFrameNoDebug(p, frameName) proc genStmtListExpr(p: BProc, n: PNode, d: var TLoc) = @@ -2615,18 +2773,22 @@ proc upConv(p: BProc, n: PNode, d: var TLoc) = initLocExpr(p, n[0], a) let dest = skipTypes(n.typ, abstractPtrs) if optObjCheck in p.options and not isObjLackingTypeField(dest): - var nilCheck = Rope(nil) - let r = rdMType(p, a, nilCheck) + var nilCheck = "" + var r = newRopeAppender() + rdMType(p, a, nilCheck, r) let checkFor = if optTinyRtti in p.config.globalOptions: genTypeInfo2Name(p.module, dest) else: genTypeInfoV1(p.module, dest, n.info) - if nilCheck != nil: - linefmt(p, cpsStmts, "if ($1 && !#isObj($2, $3)){ #raiseObjectConversionError(); $4}$n", - [nilCheck, r, checkFor, raiseInstr(p)]) + if nilCheck != "": + linefmt(p, cpsStmts, "if ($1 && !#isObj($2, $3)){ #raiseObjectConversionError(); ", + [nilCheck, r, checkFor]) else: - linefmt(p, cpsStmts, "if (!#isObj($1, $2)){ #raiseObjectConversionError(); $3}$n", - [r, checkFor, raiseInstr(p)]) + linefmt(p, cpsStmts, "if (!#isObj($1, $2)){ #raiseObjectConversionError(); ", + [r, checkFor]) + raiseInstr(p, p.s(cpsStmts)) + linefmt p, cpsStmts, "}$n", [] + if n[0].typ.kind != tyObject: if n.isLValue: putIntoDest(p, d, n, @@ -2675,8 +2837,10 @@ proc exprComplexConst(p: BProc, n: PNode, d: var TLoc) = if id == p.module.labels: # expression not found in the cache: inc(p.module.labels) - p.module.s[cfsData].addf("static NIM_CONST $1 $2 = $3;$n", - [getTypeDesc(p.module, t, skConst), tmp, genBracedInit(p, n, isConst = true, t)]) + p.module.s[cfsData].addf("static NIM_CONST $1 $2 = ", + [getTypeDesc(p.module, t, skConst), tmp]) + genBracedInit(p, n, isConst = true, t, p.module.s[cfsData]) + p.module.s[cfsData].addf(";$n", []) if d.k == locNone: fillLoc(d, locData, n, tmp, OnStatic) @@ -2691,14 +2855,15 @@ proc genConstSetup(p: BProc; sym: PSym): bool = let m = p.module useHeader(m, sym) if sym.loc.k == locNone: - fillLoc(sym.loc, locData, sym.ast, mangleName(p.module, sym), OnStatic) + fillBackendName(p.module, sym) + fillLoc(sym.loc, locData, sym.astdef, OnStatic) if m.hcrOn: incl(sym.loc.flags, lfIndirect) result = lfNoDecl notin sym.loc.flags proc genConstHeader(m, q: BModule; p: BProc, sym: PSym) = - if sym.loc.r == nil: + if sym.loc.r == "": if not genConstSetup(p, sym): return - assert(sym.loc.r != nil, $sym.name.s & $sym.itemId) + assert(sym.loc.r != "", $sym.name.s & $sym.itemId) if m.hcrOn: m.s[cfsVars].addf("static $1* $2;$n", [getTypeDesc(m, sym.loc.t, skVar), sym.loc.r]); m.initProc.procSec(cpsLocals).addf( @@ -2714,9 +2879,12 @@ proc genConstHeader(m, q: BModule; p: BProc, sym: PSym) = proc genConstDefinition(q: BModule; p: BProc; sym: PSym) = # add a suffix for hcr - will later init the global pointer with this data let actualConstName = if q.hcrOn: sym.loc.r & "_const" else: sym.loc.r - q.s[cfsData].addf("N_LIB_PRIVATE NIM_CONST $1 $2 = $3;$n", - [getTypeDesc(q, sym.typ), actualConstName, - genBracedInit(q.initProc, sym.ast, isConst = true, sym.typ)]) + var data = newRopeAppender() + data.addf("N_LIB_PRIVATE NIM_CONST $1 $2 = ", + [getTypeDesc(q, sym.typ), actualConstName]) + genBracedInit(q.initProc, sym.astdef, isConst = true, sym.typ, data) + data.addf(";$n", []) + q.s[cfsData].add data if q.hcrOn: # generate the global pointer with the real name q.s[cfsVars].addf("static $1* $2;$n", [getTypeDesc(q, sym.loc.t, skVar), sym.loc.r]) @@ -2768,15 +2936,17 @@ proc expr(p: BProc, n: PNode, d: var TLoc) = genProcPrototype(p.module, sym) else: genProc(p.module, sym) - if sym.loc.r == nil or sym.loc.lode == nil: + if sym.loc.r == "" or sym.loc.lode == nil: internalError(p.config, n.info, "expr: proc not init " & sym.name.s) putLocIntoDest(p, d, sym.loc) of skConst: if isSimpleConst(sym.typ): - putIntoDest(p, d, n, genLiteral(p, sym.ast, sym.typ), OnStatic) + var lit = newRopeAppender() + genLiteral(p, sym.astdef, sym.typ, lit) + putIntoDest(p, d, n, lit, OnStatic) elif useAliveDataFromDce in p.module.flags: genConstHeader(p.module, p.module, p, sym) - assert((sym.loc.r != nil) and (sym.loc.t != nil)) + assert((sym.loc.r != "") and (sym.loc.t != nil)) putLocIntoDest(p, d, sym.loc) else: genComplexConst(p, sym, d) @@ -2791,7 +2961,7 @@ proc expr(p: BProc, n: PNode, d: var TLoc) = if sfCompileTime in sym.flags: genSingleVar(p, sym, n, astdef(sym)) - if sym.loc.r == nil or sym.loc.t == nil: + if sym.loc.r == "" or sym.loc.t == nil: #echo "FAILED FOR PRCO ", p.prc.name.s #echo renderTree(p.prc.ast, {renderIds}) internalError p.config, n.info, "expr: var not init " & sym.name.s & "_" & $sym.id @@ -2806,17 +2976,17 @@ proc expr(p: BProc, n: PNode, d: var TLoc) = of skTemp: when false: # this is more harmful than helpful. - if sym.loc.r == nil: + if sym.loc.r == "": # we now support undeclared 'skTemp' variables for easier # transformations in other parts of the compiler: assignLocalVar(p, n) - if sym.loc.r == nil or sym.loc.t == nil: + if sym.loc.r == "" or sym.loc.t == nil: #echo "FAILED FOR PRCO ", p.prc.name.s #echo renderTree(p.prc.ast, {renderIds}) internalError(p.config, n.info, "expr: temp not init " & sym.name.s & "_" & $sym.id) putLocIntoDest(p, d, sym.loc) of skParam: - if sym.loc.r == nil or sym.loc.t == nil: + if sym.loc.r == "" or sym.loc.t == nil: # echo "FAILED FOR PRCO ", p.prc.name.s # debug p.prc.typ.n # echo renderTree(p.prc.ast, {renderIds}) @@ -2825,12 +2995,17 @@ proc expr(p: BProc, n: PNode, d: var TLoc) = else: internalError(p.config, n.info, "expr(" & $sym.kind & "); unknown symbol") of nkNilLit: if not isEmptyType(n.typ): - putIntoDest(p, d, n, genLiteral(p, n)) + var lit = newRopeAppender() + genLiteral(p, n, lit) + putIntoDest(p, d, n, lit) of nkStrLit..nkTripleStrLit: - putDataIntoDest(p, d, n, genLiteral(p, n)) - of nkIntLit..nkUInt64Lit, - nkFloatLit..nkFloat128Lit, nkCharLit: - putIntoDest(p, d, n, genLiteral(p, n)) + var lit = newRopeAppender() + genLiteral(p, n, lit) + putDataIntoDest(p, d, n, lit) + of nkIntLit..nkUInt64Lit, nkFloatLit..nkFloat128Lit, nkCharLit: + var lit = newRopeAppender() + genLiteral(p, n, lit) + putIntoDest(p, d, n, lit) of nkCall, nkHiddenCallConv, nkInfix, nkPrefix, nkPostfix, nkCommand, nkCallStrLit: genLineDir(p, n) # may be redundant, it is generated in fixupCall as well @@ -2850,7 +3025,9 @@ proc expr(p: BProc, n: PNode, d: var TLoc) = genCall(p, n, d) of nkCurly: if isDeepConstExpr(n) and n.len != 0: - putIntoDest(p, d, n, genSetNode(p, n)) + var lit = newRopeAppender() + genSetNode(p, n, lit) + putIntoDest(p, d, n, lit) else: genSetConstr(p, n, d) of nkBracket: @@ -2890,7 +3067,7 @@ proc expr(p: BProc, n: PNode, d: var TLoc) = of nkLambdaKinds: var sym = n[namePos].sym genProc(p.module, sym) - if sym.loc.r == nil or sym.loc.lode == nil: + if sym.loc.r == "" or sym.loc.lode == nil: internalError(p.config, n.info, "expr: proc not init " & sym.name.s) putLocIntoDest(p, d, sym.loc) of nkClosure: genClosure(p, n, d) @@ -2961,7 +3138,8 @@ proc expr(p: BProc, n: PNode, d: var TLoc) = if n[genericParamsPos].kind == nkEmpty: var prc = n[namePos].sym if useAliveDataFromDce in p.module.flags: - if p.module.alive.contains(prc.itemId.item) and prc.magic in {mNone, mIsolate, mFinished}: + if p.module.alive.contains(prc.itemId.item) and + prc.magic in generatedMagics: genProc(p.module, prc) elif prc.skipGenericOwner.kind == skModule and sfCompileTime notin prc.flags: if ({sfExportc, sfCompilerProc} * prc.flags == {sfExportc}) or @@ -2982,52 +3160,48 @@ proc expr(p: BProc, n: PNode, d: var TLoc) = of nkMixinStmt, nkBindStmt: discard else: internalError(p.config, n.info, "expr(" & $n.kind & "); unknown node kind") -proc genNamedConstExpr(p: BProc, n: PNode; isConst: bool): Rope = - if n.kind == nkExprColonExpr: result = genBracedInit(p, n[1], isConst, n[0].typ) - else: result = genBracedInit(p, n, isConst, n.typ) - -proc getDefaultValue(p: BProc; typ: PType; info: TLineInfo): Rope = +proc getDefaultValue(p: BProc; typ: PType; info: TLineInfo; result: var Rope) = var t = skipTypes(typ, abstractRange+{tyOwned}-{tyTypeDesc}) case t.kind - of tyBool: result = rope"NIM_FALSE" - of tyEnum, tyChar, tyInt..tyInt64, tyUInt..tyUInt64: result = rope"0" - of tyFloat..tyFloat128: result = rope"0.0" + of tyBool: result.add rope"NIM_FALSE" + of tyEnum, tyChar, tyInt..tyInt64, tyUInt..tyUInt64: result.add rope"0" + of tyFloat..tyFloat128: result.add rope"0.0" of tyCstring, tyVar, tyLent, tyPointer, tyPtr, tyUntyped, tyTyped, tyTypeDesc, tyStatic, tyRef, tyNil: - result = rope"NIM_NIL" + result.add rope"NIM_NIL" of tyString, tySequence: if optSeqDestructors in p.config.globalOptions: - result = rope"{0, NIM_NIL}" + result.add "{0, NIM_NIL}" else: - result = rope"NIM_NIL" + result.add "NIM_NIL" of tyProc: if t.callConv != ccClosure: - result = rope"NIM_NIL" + result.add "NIM_NIL" else: - result = rope"{NIM_NIL, NIM_NIL}" + result.add "{NIM_NIL, NIM_NIL}" of tyObject: var count = 0 result.add "{" getNullValueAuxT(p, t, t, t.n, nil, result, count, true, info) result.add "}" of tyTuple: - result = rope"{" + result.add "{" for i in 0.. 0: result.add ", " - result.add getDefaultValue(p, t[i], info) + getDefaultValue(p, t[i], info, result) result.add "}" of tyArray: - result = rope"{" + result.add "{" for i in 0.. 0: result.add ", " - result.add getDefaultValue(p, t.sons[1], info) + getDefaultValue(p, t.sons[1], info, result) result.add "}" #result = rope"{}" of tyOpenArray, tyVarargs: - result = rope"{NIM_NIL, 0}" + result.add "{NIM_NIL, 0}" of tySet: - if mapSetType(p.config, t) == ctArray: result = rope"{}" - else: result = rope"0" + if mapSetType(p.config, t) == ctArray: result.add "{}" + else: result.add "0" else: globalError(p.config, info, "cannot create null element for: " & $t.kind) @@ -3091,13 +3265,13 @@ proc getNullValueAux(p: BProc; t: PType; obj, constOrNil: PNode, for i in 1.. 0: result.add ",\n" - if it.kind == nkExprColonExpr: result.add genBracedInit(p, it[1], isConst, it[0].typ) - else: result.add genBracedInit(p, it, isConst, it.typ) + if it.kind == nkExprColonExpr: genBracedInit(p, it[1], isConst, it[0].typ, result) + else: genBracedInit(p, it, isConst, it.typ, result) result.add("}\n") -proc genConstTuple(p: BProc, n: PNode; isConst: bool; tup: PType): Rope = - result = rope("{") +proc genConstTuple(p: BProc, n: PNode; isConst: bool; tup: PType; result: var Rope) = + result.add "{" for i in 0.. 0: result.add ",\n" - if it.kind == nkExprColonExpr: result.add genBracedInit(p, it[1], isConst, tup[i]) - else: result.add genBracedInit(p, it, isConst, tup[i]) + if it.kind == nkExprColonExpr: genBracedInit(p, it[1], isConst, tup[i], result) + else: genBracedInit(p, it, isConst, tup[i], result) result.add("}\n") -proc genConstSeq(p: BProc, n: PNode, t: PType; isConst: bool): Rope = +proc genConstSeq(p: BProc, n: PNode, t: PType; isConst: bool; result: var Rope) = var data = "{{$1, $1 | NIM_STRLIT_FLAG}" % [n.len.rope] let base = t.skipTypes(abstractInst)[0] if n.len > 0: @@ -3159,44 +3333,44 @@ proc genConstSeq(p: BProc, n: PNode, t: PType; isConst: bool): Rope = data.add(", {") for i in 0.. 0: data.addf(",$n", []) - data.add genBracedInit(p, n[i], isConst, base) + genBracedInit(p, n[i], isConst, base, data) data.add("}") data.add("}") - result = getTempName(p.module) + let tmpName = getTempName(p.module) - appcg(p.module, cfsData, + appcg(p.module, cfsStrData, "static $5 struct {$n" & " #TGenericSeq Sup;$n" & " $1 data[$2];$n" & "} $3 = $4;$n", [ - getTypeDesc(p.module, base), n.len, result, data, + getTypeDesc(p.module, base), n.len, tmpName, data, if isConst: "NIM_CONST" else: ""]) - result = "(($1)&$2)" % [getTypeDesc(p.module, t), result] + result.add "(($1)&$2)" % [getTypeDesc(p.module, t), tmpName] -proc genConstSeqV2(p: BProc, n: PNode, t: PType; isConst: bool): Rope = +proc genConstSeqV2(p: BProc, n: PNode, t: PType; isConst: bool; result: var Rope) = let base = t.skipTypes(abstractInst)[0] var data = rope"{" for i in 0.. 0: data.addf(",$n", []) - data.add genBracedInit(p, n[i], isConst, base) + genBracedInit(p, n[i], isConst, base, data) data.add("}") let payload = getTempName(p.module) - appcg(p.module, cfsData, + appcg(p.module, cfsStrData, "static $5 struct {$n" & " NI cap; $1 data[$2];$n" & "} $3 = {$2 | NIM_STRLIT_FLAG, $4};$n", [ getTypeDesc(p.module, base), n.len, payload, data, if isConst: "const" else: ""]) - result = "{$1, ($2*)&$3}" % [rope(n.len), getSeqPayloadType(p.module, t), payload] + result.add "{$1, ($2*)&$3}" % [rope(n.len), getSeqPayloadType(p.module, t), payload] -proc genBracedInit(p: BProc, n: PNode; isConst: bool; optionalType: PType): Rope = +proc genBracedInit(p: BProc, n: PNode; isConst: bool; optionalType: PType; result: var Rope) = case n.kind of nkHiddenStdConv, nkHiddenSubConv: - result = genBracedInit(p, n[1], isConst, n.typ) + genBracedInit(p, n[1], isConst, n.typ, result) else: var ty = tyNone var typ: PType = nil @@ -3211,12 +3385,12 @@ proc genBracedInit(p: BProc, n: PNode; isConst: bool; optionalType: PType): Rope case ty of tySet: let cs = toBitSet(p.config, n) - result = genRawSetData(cs, int(getSize(p.config, n.typ))) + genRawSetData(cs, int(getSize(p.config, n.typ)), result) of tySequence: if optSeqDestructors in p.config.globalOptions: - result = genConstSeqV2(p, n, typ, isConst) + genConstSeqV2(p, n, typ, isConst, result) else: - result = genConstSeq(p, n, typ, isConst) + genConstSeq(p, n, typ, isConst, result) of tyProc: if typ.callConv == ccClosure and n.safeLen > 1 and n[1].kind == nkNilLit: # n.kind could be: nkClosure, nkTupleConstr and maybe others; `n.safeLen` @@ -3229,44 +3403,45 @@ proc genBracedInit(p: BProc, n: PNode; isConst: bool; optionalType: PType): Rope # leading to duplicate code like this: # "{NIM_NIL,NIM_NIL}, {NIM_NIL,NIM_NIL}" if n[0].kind == nkNilLit: - result = ~"{NIM_NIL,NIM_NIL}" + result.add "{NIM_NIL,NIM_NIL}" else: var d: TLoc initLocExpr(p, n[0], d) - result = "{(($1) $2),NIM_NIL}" % [getClosureType(p.module, typ, clHalfWithEnv), rdLoc(d)] + result.add "{(($1) $2),NIM_NIL}" % [getClosureType(p.module, typ, clHalfWithEnv), rdLoc(d)] else: var d: TLoc initLocExpr(p, n, d) - result = rdLoc(d) + result.add rdLoc(d) of tyArray, tyVarargs: - result = genConstSimpleList(p, n, isConst) + genConstSimpleList(p, n, isConst, result) of tyTuple: - result = genConstTuple(p, n, isConst, typ) + genConstTuple(p, n, isConst, typ, result) of tyOpenArray: if n.kind != nkBracket: internalError(p.config, n.info, "const openArray expression is not an array construction") - let data = genConstSimpleList(p, n, isConst) + var data = newRopeAppender() + genConstSimpleList(p, n, isConst, data) let payload = getTempName(p.module) let ctype = getTypeDesc(p.module, typ[0]) let arrLen = n.len - appcg(p.module, cfsData, + appcg(p.module, cfsStrData, "static $5 $1 $3[$2] = $4;$n", [ ctype, arrLen, payload, data, if isConst: "const" else: ""]) - result = "{($1*)&$2, $3}" % [ctype, payload, rope arrLen] + result.add "{($1*)&$2, $3}" % [ctype, payload, rope arrLen] of tyObject: - result = genConstObjConstr(p, n, isConst) + genConstObjConstr(p, n, isConst, result) of tyString, tyCstring: if optSeqDestructors in p.config.globalOptions and n.kind != nkNilLit and ty == tyString: - result = genStringLiteralV2Const(p.module, n, isConst) + genStringLiteralV2Const(p.module, n, isConst, result) else: var d: TLoc initLocExpr(p, n, d) - result = rdLoc(d) + result.add rdLoc(d) else: var d: TLoc initLocExpr(p, n, d) - result = rdLoc(d) + result.add rdLoc(d) diff --git a/compiler/ccgliterals.nim b/compiler/ccgliterals.nim index ee56da586565..cbef6771f6b3 100644 --- a/compiler/ccgliterals.nim +++ b/compiler/ccgliterals.nim @@ -21,7 +21,7 @@ template detectVersion(field, corename) = if core == nil or core.kind != skConst: m.g.field = 1 else: - m.g.field = toInt(ast.getInt(core.ast)) + m.g.field = toInt(ast.getInt(core.astdef)) result = m.g.field proc detectStrVersion(m: BModule): int = @@ -32,82 +32,87 @@ proc detectSeqVersion(m: BModule): int = # ----- Version 1: GC'ed strings and seqs -------------------------------- -proc genStringLiteralDataOnlyV1(m: BModule, s: string): Rope = - discard cgsym(m, "TGenericSeq") - result = getTempName(m) - m.s[cfsData].addf("STRING_LITERAL($1, $2, $3);$n", - [result, makeCString(s), rope(s.len)]) +proc genStringLiteralDataOnlyV1(m: BModule, s: string; result: var Rope) = + cgsym(m, "TGenericSeq") + let tmp = getTempName(m) + result.add tmp + m.s[cfsStrData].addf("STRING_LITERAL($1, $2, $3);$n", + [tmp, makeCString(s), rope(s.len)]) -proc genStringLiteralV1(m: BModule; n: PNode): Rope = +proc genStringLiteralV1(m: BModule; n: PNode; result: var Rope) = if s.isNil: - result = ropecg(m, "((#NimStringDesc*) NIM_NIL)", []) + appcg(m, result, "((#NimStringDesc*) NIM_NIL)", []) else: let id = nodeTableTestOrSet(m.dataCache, n, m.labels) if id == m.labels: # string literal not found in the cache: - result = ropecg(m, "((#NimStringDesc*) &$1)", - [genStringLiteralDataOnlyV1(m, n.strVal)]) + appcg(m, result, "((#NimStringDesc*) &", []) + genStringLiteralDataOnlyV1(m, n.strVal, result) + result.add ")" else: - result = ropecg(m, "((#NimStringDesc*) &$1$2)", + appcg(m, result, "((#NimStringDesc*) &$1$2)", [m.tmpBase, id]) # ------ Version 2: destructor based strings and seqs ----------------------- proc genStringLiteralDataOnlyV2(m: BModule, s: string; result: Rope; isConst: bool) = - m.s[cfsData].addf("static $4 struct {$n" & + m.s[cfsStrData].addf("static $4 struct {$n" & " NI cap; NIM_CHAR data[$2+1];$n" & "} $1 = { $2 | NIM_STRLIT_FLAG, $3 };$n", [result, rope(s.len), makeCString(s), rope(if isConst: "const" else: "")]) -proc genStringLiteralV2(m: BModule; n: PNode; isConst: bool): Rope = +proc genStringLiteralV2(m: BModule; n: PNode; isConst: bool; result: var Rope) = let id = nodeTableTestOrSet(m.dataCache, n, m.labels) if id == m.labels: let pureLit = getTempName(m) genStringLiteralDataOnlyV2(m, n.strVal, pureLit, isConst) - result = getTempName(m) - discard cgsym(m, "NimStrPayload") - discard cgsym(m, "NimStringV2") + let tmp = getTempName(m) + result.add tmp + cgsym(m, "NimStrPayload") + cgsym(m, "NimStringV2") # string literal not found in the cache: - m.s[cfsData].addf("static $4 NimStringV2 $1 = {$2, (NimStrPayload*)&$3};$n", - [result, rope(n.strVal.len), pureLit, rope(if isConst: "const" else: "")]) + m.s[cfsStrData].addf("static $4 NimStringV2 $1 = {$2, (NimStrPayload*)&$3};$n", + [tmp, rope(n.strVal.len), pureLit, rope(if isConst: "const" else: "")]) else: - result = getTempName(m) - m.s[cfsData].addf("static $4 NimStringV2 $1 = {$2, (NimStrPayload*)&$3};$n", - [result, rope(n.strVal.len), m.tmpBase & rope(id), + let tmp = getTempName(m) + result.add tmp + m.s[cfsStrData].addf("static $4 NimStringV2 $1 = {$2, (NimStrPayload*)&$3};$n", + [tmp, rope(n.strVal.len), m.tmpBase & rope(id), rope(if isConst: "const" else: "")]) -proc genStringLiteralV2Const(m: BModule; n: PNode; isConst: bool): Rope = +proc genStringLiteralV2Const(m: BModule; n: PNode; isConst: bool; result: var Rope) = let id = nodeTableTestOrSet(m.dataCache, n, m.labels) var pureLit: Rope if id == m.labels: pureLit = getTempName(m) - discard cgsym(m, "NimStrPayload") - discard cgsym(m, "NimStringV2") + cgsym(m, "NimStrPayload") + cgsym(m, "NimStringV2") # string literal not found in the cache: genStringLiteralDataOnlyV2(m, n.strVal, pureLit, isConst) else: pureLit = m.tmpBase & rope(id) - result = "{$1, (NimStrPayload*)&$2}" % [rope(n.strVal.len), pureLit] + result.addf "{$1, (NimStrPayload*)&$2}", [rope(n.strVal.len), pureLit] # ------ Version selector --------------------------------------------------- proc genStringLiteralDataOnly(m: BModule; s: string; info: TLineInfo; - isConst: bool): Rope = + isConst: bool; result: var Rope) = case detectStrVersion(m) - of 0, 1: result = genStringLiteralDataOnlyV1(m, s) + of 0, 1: genStringLiteralDataOnlyV1(m, s, result) of 2: - result = getTempName(m) - genStringLiteralDataOnlyV2(m, s, result, isConst) + let tmp = getTempName(m) + genStringLiteralDataOnlyV2(m, s, tmp, isConst) + result.add tmp else: localError(m.config, info, "cannot determine how to produce code for string literal") -proc genNilStringLiteral(m: BModule; info: TLineInfo): Rope = - result = ropecg(m, "((#NimStringDesc*) NIM_NIL)", []) +proc genNilStringLiteral(m: BModule; info: TLineInfo; result: var Rope) = + appcg(m, result, "((#NimStringDesc*) NIM_NIL)", []) -proc genStringLiteral(m: BModule; n: PNode): Rope = +proc genStringLiteral(m: BModule; n: PNode; result: var Rope) = case detectStrVersion(m) - of 0, 1: result = genStringLiteralV1(m, n) - of 2: result = genStringLiteralV2(m, n, isConst = true) + of 0, 1: genStringLiteralV1(m, n, result) + of 2: genStringLiteralV2(m, n, isConst = true, result) else: localError(m.config, n.info, "cannot determine how to produce code for string literal") diff --git a/compiler/ccgreset.nim b/compiler/ccgreset.nim index fc7370d8121f..5e6456704dd2 100644 --- a/compiler/ccgreset.nim +++ b/compiler/ccgreset.nim @@ -24,7 +24,7 @@ proc specializeResetN(p: BProc, accessor: Rope, n: PNode; of nkRecCase: if (n[0].kind != nkSym): internalError(p.config, n.info, "specializeResetN") let disc = n[0].sym - if disc.loc.r == nil: fillObjectFields(p.module, typ) + if disc.loc.r == "": fillObjectFields(p.module, typ) if disc.loc.t == nil: internalError(p.config, n.info, "specializeResetN()") lineF(p, cpsStmts, "switch ($1.$2) {$n", [accessor, disc.loc.r]) @@ -42,7 +42,7 @@ proc specializeResetN(p: BProc, accessor: Rope, n: PNode; of nkSym: let field = n.sym if field.typ.kind == tyVoid: return - if field.loc.r == nil: fillObjectFields(p.module, typ) + if field.loc.r == "": fillObjectFields(p.module, typ) if field.loc.t == nil: internalError(p.config, n.info, "specializeResetN()") specializeResetT(p, "$1.$2" % [accessor, field.loc.r], field.loc.t) @@ -87,7 +87,20 @@ proc specializeResetT(p: BProc, accessor: Rope, typ: PType) = lineCg(p, cpsStmts, "$1 = 0;$n", [accessor]) of tyCstring, tyPointer, tyPtr, tyVar, tyLent: lineCg(p, cpsStmts, "$1 = NIM_NIL;$n", [accessor]) - else: + of tySet: + case mapSetType(p.config, typ) + of ctArray: + lineCg(p, cpsStmts, "#nimZeroMem($1, sizeof($2));$n", + [accessor, getTypeDesc(p.module, typ)]) + of ctInt8, ctInt16, ctInt32, ctInt64: + lineCg(p, cpsStmts, "$1 = 0;$n", [accessor]) + else: + doAssert false, "unexpected set type kind" + of {tyNone, tyEmpty, tyNil, tyUntyped, tyTyped, tyGenericInvocation, + tyGenericParam, tyOrdinal, tyRange, tyOpenArray, tyForward, tyVarargs, + tyUncheckedArray, tyProxy, tyBuiltInTypeClass, tyUserTypeClass, + tyUserTypeClassInst, tyCompositeTypeClass, tyAnd, tyOr, tyNot, + tyAnything, tyStatic, tyFromExpr, tyConcept, tyVoid, tyIterable}: discard proc specializeReset(p: BProc, a: TLoc) = diff --git a/compiler/ccgstmts.nim b/compiler/ccgstmts.nim index 240fa55c8814..7b5f4ff72332 100644 --- a/compiler/ccgstmts.nim +++ b/compiler/ccgstmts.nim @@ -15,21 +15,22 @@ const stringCaseThreshold = 8 # above X strings a hash-switch for strings is generated -proc getTraverseProc(p: BProc, v: PSym): Rope = - if p.config.selectedGC in {gcMarkAndSweep, gcHooks, gcV2, gcRefc} and +proc registerTraverseProc(p: BProc, v: PSym) = + var traverseProc = "" + if p.config.selectedGC in {gcMarkAndSweep, gcHooks, gcRefc} and optOwnedRefs notin p.config.globalOptions and containsGarbageCollectedRef(v.loc.t): # we register a specialized marked proc here; this has the advantage # that it works out of the box for thread local storage then :-) - result = genTraverseProcForGlobal(p.module, v, v.info) + traverseProc = genTraverseProcForGlobal(p.module, v, v.info) -proc registerTraverseProc(p: BProc, v: PSym, traverseProc: Rope) = - if sfThread in v.flags: - appcg(p.module, p.module.preInitProc.procSec(cpsInit), - "$n\t#nimRegisterThreadLocalMarker($1);$n$n", [traverseProc]) - else: - appcg(p.module, p.module.preInitProc.procSec(cpsInit), - "$n\t#nimRegisterGlobalMarker($1);$n$n", [traverseProc]) + if traverseProc.len != 0 and not p.hcrOn: + if sfThread in v.flags: + appcg(p.module, p.module.preInitProc.procSec(cpsInit), + "$n\t#nimRegisterThreadLocalMarker($1);$n$n", [traverseProc]) + else: + appcg(p.module, p.module.preInitProc.procSec(cpsInit), + "$n\t#nimRegisterGlobalMarker($1);$n$n", [traverseProc]) proc isAssignedImmediately(conf: ConfigRef; n: PNode): bool {.inline.} = if n.kind == nkEmpty: @@ -54,7 +55,9 @@ proc inExceptBlockLen(p: BProc): int = proc startBlockInternal(p: BProc): int {.discardable.} = inc(p.labels) result = p.blocks.len - setLen(p.blocks, result + 1) + + p.blocks.add initBlock() + p.blocks[result].id = p.labels p.blocks[result].nestedTryStmts = p.nestedTryStmts.len.int16 p.blocks[result].nestedExceptStmts = p.inExceptBlockLen.int16 @@ -78,7 +81,7 @@ proc genVarTuple(p: BProc, n: PNode) = # check only the first son var forHcr = treatGlobalDifferentlyForHCR(p.module, n[0].sym) - let hcrCond = if forHcr: getTempName(p.module) else: nil + let hcrCond = if forHcr: getTempName(p.module) else: "" var hcrGlobals: seq[tuple[loc: TLoc, tp: Rope]] # determine if the tuple is constructed at top-level scope or inside of a block (if/while/block) let isGlobalInBlock = forHcr and p.blocks.len > 2 @@ -97,13 +100,10 @@ proc genVarTuple(p: BProc, n: PNode) = let vn = n[i] let v = vn.sym if sfCompileTime in v.flags: continue - var traverseProc: Rope if sfGlobal in v.flags: - assignGlobalVar(p, vn, nil) + assignGlobalVar(p, vn, "") genObjectInit(p, cpsInit, v.typ, v.loc, constructObj) - traverseProc = getTraverseProc(p, v) - if traverseProc != nil and not p.hcrOn: - registerTraverseProc(p, v, traverseProc) + registerTraverseProc(p, v) else: assignLocalVar(p, vn) initLocalVar(p, v, immediateAsgn=isAssignedImmediately(p.config, n[^1])) @@ -115,7 +115,7 @@ proc genVarTuple(p: BProc, n: PNode) = field.r = "$1.$2" % [rdLoc(tup), mangleRecFieldName(p.module, t.n[i].sym)] putLocIntoDest(p, v.loc, field) if forHcr or isGlobalInBlock: - hcrGlobals.add((loc: v.loc, tp: if traverseProc == nil: ~"NULL" else: traverseProc)) + hcrGlobals.add((loc: v.loc, tp: "NULL")) if forHcr: # end the block where the tuple gets initialized @@ -145,12 +145,12 @@ proc loadInto(p: BProc, le, ri: PNode, a: var TLoc) {.inline.} = a.flags.incl(lfEnforceDeref) expr(p, ri, a) -proc assignLabel(b: var TBlock): Rope {.inline.} = +proc assignLabel(b: var TBlock; result: var Rope) {.inline.} = b.label = "LA" & b.id.rope - result = b.label + result.add b.label -proc blockBody(b: var TBlock): Rope = - result = b.sections[cpsLocals] +proc blockBody(b: var TBlock; result: var Rope) = + result.add b.sections[cpsLocals] if b.frameLen > 0: result.addf("FR_.len+=$1;$n", [b.frameLen.rope]) result.add(b.sections[cpsInit]) @@ -159,7 +159,7 @@ proc blockBody(b: var TBlock): Rope = proc endBlock(p: BProc, blockEnd: Rope) = let topBlock = p.blocks.len-1 # the block is merged into the parent block - p.blocks[topBlock-1].sections[cpsStmts].add(p.blocks[topBlock].blockBody) + p.blocks[topBlock].blockBody(p.blocks[topBlock-1].sections[cpsStmts]) setLen(p.blocks, topBlock) # this is done after the block is popped so $n is # properly indented when pretty printing is enabled @@ -171,7 +171,7 @@ proc endBlock(p: BProc) = var blockEnd: Rope if frameLen > 0: blockEnd.addf("FR_.len-=$1;$n", [frameLen.rope]) - if p.blocks[topBlock].label != nil: + if p.blocks[topBlock].label.len != 0: blockEnd.addf("} $1: ;$n", [p.blocks[topBlock].label]) else: blockEnd.addf("}$n", []) @@ -279,17 +279,15 @@ proc genGotoVar(p: BProc; value: PNode) = else: lineF(p, cpsStmts, "goto NIMSTATE_$#;$n", [value.intVal.rope]) -proc genBracedInit(p: BProc, n: PNode; isConst: bool; optionalType: PType): Rope +proc genBracedInit(p: BProc, n: PNode; isConst: bool; optionalType: PType; result: var Rope) -proc potentialValueInit(p: BProc; v: PSym; value: PNode): Rope = +proc potentialValueInit(p: BProc; v: PSym; value: PNode; result: var Rope) = if lfDynamicLib in v.loc.flags or sfThread in v.flags or p.hcrOn: - result = nil + discard "nothing to do" elif sfGlobal in v.flags and value != nil and isDeepConstExpr(value, p.module.compileToCpp) and p.withinLoop == 0 and not containsGarbageCollectedRef(v.typ): #echo "New code produced for ", v.name.s, " ", p.config $ value.info - result = genBracedInit(p, value, isConst = false, v.typ) - else: - result = nil + genBracedInit(p, value, isConst = false, v.typ, result) proc genSingleVar(p: BProc, v: PSym; vn, value: PNode) = if sfGoto in v.flags: @@ -297,8 +295,8 @@ proc genSingleVar(p: BProc, v: PSym; vn, value: PNode) = genGotoVar(p, value) return var targetProc = p - var traverseProc: Rope - let valueAsRope = potentialValueInit(p, v, value) + var valueAsRope = "" + potentialValueInit(p, v, value, valueAsRope) if sfGlobal in v.flags: if v.flags * {sfImportc, sfExportc} == {sfImportc} and value.kind == nkEmpty and @@ -314,7 +312,7 @@ proc genSingleVar(p: BProc, v: PSym; vn, value: PNode) = # That's why we are doing the construction inside the preInitProc. # genObjectInit relies on the C runtime's guarantees that # global variables will be initialized to zero. - if valueAsRope == nil: + if valueAsRope.len == 0: var loc = v.loc # When the native TLS is unavailable, a global thread-local variable needs @@ -328,9 +326,7 @@ proc genSingleVar(p: BProc, v: PSym; vn, value: PNode) = # if sfImportc notin v.flags: constructLoc(p.module.preInitProc, v.loc) if sfExportc in v.flags and p.module.g.generatedHeader != nil: genVarPrototype(p.module.g.generatedHeader, vn) - traverseProc = getTraverseProc(p, v) - if traverseProc != nil and not p.hcrOn: - registerTraverseProc(p, v, traverseProc) + registerTraverseProc(p, v) else: let imm = isAssignedImmediately(p.config, value) if imm and p.module.compileToCpp and p.splitDecls == 0 and @@ -343,14 +339,14 @@ proc genSingleVar(p: BProc, v: PSym; vn, value: PNode) = var tmp: TLoc if value.kind in nkCallKinds and value[0].kind == nkSym and sfConstructor in value[0].sym.flags: - var params: Rope + var params = newRopeAppender() + var argsCounter = 0 let typ = skipTypes(value[0].typ, abstractInst) assert(typ.kind == tyProc) for i in 1..= 0 and not p.blocks[idx].isLoop: dec idx if idx < 0 or not p.blocks[idx].isLoop: internalError(p.config, t.info, "no loop to break") - let label = assignLabel(p.blocks[idx]) + p.blocks[idx].label = "LA" & p.blocks[idx].id.rope blockLeaveActions(p, p.nestedTryStmts.len - p.blocks[idx].nestedTryStmts, p.inExceptBlockLen - p.blocks[idx].nestedExceptStmts) genLineDir(p, t) - lineF(p, cpsStmts, "goto $1;$n", [label]) + lineF(p, cpsStmts, "goto $1;$n", [p.blocks[idx].label]) proc raiseExit(p: BProc) = assert p.config.exc == excGoto @@ -729,21 +728,19 @@ proc finallyActions(p: BProc) = if finallyBlock != nil: genSimpleBlock(p, finallyBlock[0]) -proc raiseInstr(p: BProc): Rope = +proc raiseInstr(p: BProc; result: var Rope) = if p.config.exc == excGoto: let L = p.nestedTryStmts.len if L == 0: p.flags.incl beforeRetNeeded # easy case, simply goto 'ret': - result = ropecg(p.module, "goto BeforeRet_;$n", []) + result.add ropecg(p.module, "goto BeforeRet_;$n", []) else: # raise inside an 'except' must go to the finally block, # raise outside an 'except' block must go to the 'except' list. - result = ropecg(p.module, "goto LA$1_;$n", + result.add ropecg(p.module, "goto LA$1_;$n", [p.nestedTryStmts[L-1].label]) # + ord(p.nestedTryStmts[L-1].inExcept)]) - else: - result = nil proc genRaiseStmt(p: BProc, t: PNode) = if t[0].kind != nkEmpty: @@ -772,12 +769,10 @@ proc genRaiseStmt(p: BProc, t: PNode) = genLineDir(p, t) # reraise the last exception: if p.config.exc == excCpp: - line(p, cpsStmts, ~"throw;$n") + line(p, cpsStmts, "throw;\n") else: linefmt(p, cpsStmts, "#reraiseException();$n", []) - let gotoInstr = raiseInstr(p) - if gotoInstr != nil: - line(p, cpsStmts, gotoInstr) + raiseInstr(p, p.s(cpsStmts)) template genCaseGenericBranch(p: BProc, b: PNode, e: TLoc, rangeFormat, eqFormat: FormatStr, labl: TLabel) = @@ -837,17 +832,27 @@ template genCaseGeneric(p: BProc, t: PNode, d: var TLoc, fixLabel(p, lend) proc genCaseStringBranch(p: BProc, b: PNode, e: TLoc, labl: TLabel, + stringKind: TTypeKind, branches: var openArray[Rope]) = var x: TLoc for i in 0.. 0: genIfForCaseUntil(p, n, d, rangeFormat = "if ($1 >= $2 && $1 <= $3) goto $4;$n", eqFormat = "if ($1 == $2) goto $3;$n", - splitPoint, a) else: nil + splitPoint, a) else: "" # generate switch part (might be empty): if splitPoint+1 < n.len: @@ -946,7 +966,7 @@ proc genOrdinalCase(p: BProc, n: PNode, d: var TLoc) = if (hasAssume in CC[p.config.cCompiler].props) and not hasDefault: lineF(p, cpsStmts, "default: __assume(0);$n", []) lineF(p, cpsStmts, "}$n", []) - if lend != nil: fixLabel(p, lend) + if lend != "": fixLabel(p, lend) proc genCase(p: BProc, t: PNode, d: var TLoc) = genLineDir(p, t) @@ -954,7 +974,9 @@ proc genCase(p: BProc, t: PNode, d: var TLoc) = getTemp(p, t.typ, d) case skipTypes(t[0].typ, abstractVarRange).kind of tyString: - genStringCase(p, t, d) + genStringCase(p, t, tyString, d) + of tyCstring: + genStringCase(p, t, tyCstring, d) of tyFloat..tyFloat128: genCaseGeneric(p, t, d, "if ($1 >= $2 && $1 <= $3) goto $4;$n", "if ($1 == $2) goto $3;$n") @@ -1045,7 +1067,7 @@ proc genTryCpp(p: BProc, t: PNode, d: var TLoc) = linefmt(p, cpsStmts, "#popCurrentException();$n", []) endBlock(p) else: - var orExpr = Rope(nil) + var orExpr = newRopeAppender() var exvar = PNode(nil) for j in 0..$1, $2)", [memberName, checkFor]) - if orExpr != nil: + if orExpr.len != 0: if hasIf: startBlock(p, "else if ($1) {$n", [orExpr]) else: startBlock(p, "if ($1) {$n", [orExpr]) hasIf = true if exvar != nil: - fillLoc(exvar.sym.loc, locTemp, exvar, mangleLocalName(p, exvar.sym), OnStack) + fillLocalName(p, exvar.sym) + fillLoc(exvar.sym.loc, locTemp, exvar, OnStack) linefmt(p, cpsStmts, "$1 $2 = T$3_;$n", [getTypeDesc(p.module, exvar.sym.typ), rdLoc(exvar.sym.loc), rope(etmp+1)]) # we handled the error: @@ -1112,7 +1135,8 @@ proc genTryCpp(p: BProc, t: PNode, d: var TLoc) = typeNode = t[i][j][1] if isImportedException(typeNode.typ, p.config): let exvar = t[i][j][2] # ex1 in `except ExceptType as ex1:` - fillLoc(exvar.sym.loc, locTemp, exvar, mangleLocalName(p, exvar.sym), OnStack) + fillLocalName(p, exvar.sym) + fillLoc(exvar.sym.loc, locTemp, exvar, OnStack) startBlock(p, "catch ($1& $2) {$n", getTypeDesc(p.module, typeNode.typ), rdLoc(exvar.sym.loc)) genExceptBranchBody(t[i][^1]) # exception handler body will duplicated for every type endBlock(p) @@ -1165,7 +1189,7 @@ proc genTryCppOld(p: BProc, t: PNode, d: var TLoc) = if not isEmptyType(t.typ) and d.k == locNone: getTemp(p, t.typ, d) genLineDir(p, t) - discard cgsym(p.module, "popCurrentExceptionEx") + cgsym(p.module, "popCurrentExceptionEx") let fin = if t[^1].kind == nkFinally: t[^1] else: nil p.nestedTryStmts.add((fin, false, 0.Natural)) startBlock(p, "try {$n") @@ -1191,7 +1215,8 @@ proc genTryCppOld(p: BProc, t: PNode, d: var TLoc) = for j in 0.. error. if m.hcrOn or isKeyword(s.name) or m.g.config.cppDefines.contains(res): res.add "_0" - result = res.rope - s.loc.r = result + s.loc.r = res.rope writeMangledName(m.ndi, s, m.config) -proc mangleLocalName(p: BProc; s: PSym): Rope = +proc fillLocalName(p: BProc; s: PSym) = assert s.kind in skLocalVars+{skTemp} #assert sfGlobal notin s.flags - result = s.loc.r - if result == nil: + if s.loc.r == "": var key = s.name.s.mangle - shallow(key) let counter = p.sigConflicts.getOrDefault(key) - result = key.rope + var result = key.rope if s.kind == skTemp: # speed up conflict search for temps (these are quite common): if counter != 0: result.add "_" & rope(counter+1) @@ -102,7 +95,6 @@ proc scopeMangledParam(p: BProc; param: PSym) = ## generate unique identifiers reliably (consider that ``var a = a`` is ## even an idiom in Nim). var key = param.name.s.mangle - shallow(key) p.sigConflicts.inc(key) const @@ -110,13 +102,12 @@ const tyDistinct, tyRange, tyStatic, tyAlias, tySink, tyInferred, tyOwned} -proc typeName(typ: PType): Rope = +proc typeName(typ: PType; result: var Rope) = let typ = typ.skipTypes(irrelevantForBackend) - result = - if typ.sym != nil and typ.kind in {tyObject, tyEnum}: - rope($typ.kind & '_' & typ.sym.name.s.mangle) - else: - rope($typ.kind) + result.add $typ.kind + if typ.sym != nil and typ.kind in {tyObject, tyEnum}: + result.add "_" + result.add typ.sym.name.s.mangle proc getTypeName(m: BModule; typ: PType; sig: SigHash): Rope = var t = typ @@ -129,14 +120,17 @@ proc getTypeName(m: BModule; typ: PType; sig: SigHash): Rope = else: break let typ = if typ.kind in {tyAlias, tySink, tyOwned}: typ.lastSon else: typ - if typ.loc.r == nil: - typ.loc.r = typ.typeName & $sig + if typ.loc.r == "": + typ.typeName(typ.loc.r) + typ.loc.r.add $sig else: when defined(debugSigHashes): # check consistency: - assert($typ.loc.r == $(typ.typeName & $sig)) + var tn = newRopeAppender() + typ.typeName(tn) + assert($typ.loc.r == $(tn & $sig)) result = typ.loc.r - if result == nil: internalError(m.config, "getTypeName: " & $typ.kind) + if result == "": internalError(m.config, "getTypeName: " & $typ.kind) proc mapSetType(conf: ConfigRef; typ: PType): TCTypeKind = case int(getSize(conf, typ)) @@ -264,7 +258,7 @@ proc addAbiCheck(m: BModule, t: PType, name: Rope) = proc fillResult(conf: ConfigRef; param: PNode, proctype: PType) = - fillLoc(param.sym.loc, locParam, param, ~"Result", + fillLoc(param.sym.loc, locParam, param, "Result", OnStack) let t = param.sym.typ if mapReturnType(conf, t) != ctArray and isInvalidReturnType(conf, proctype): @@ -290,11 +284,11 @@ proc getSimpleTypeDesc(m: BModule, typ: PType): Rope = of tyString: case detectStrVersion(m) of 2: - discard cgsym(m, "NimStrPayload") - discard cgsym(m, "NimStringV2") + cgsym(m, "NimStrPayload") + cgsym(m, "NimStringV2") result = typeNameOrLiteral(m, typ, "NimStringV2") else: - discard cgsym(m, "NimStringDesc") + cgsym(m, "NimStringDesc") result = typeNameOrLiteral(m, typ, "NimStringDesc*") of tyCstring: result = typeNameOrLiteral(m, typ, "NCSTRING") of tyBool: result = typeNameOrLiteral(m, typ, "NIM_BOOL") @@ -308,11 +302,11 @@ proc getSimpleTypeDesc(m: BModule, typ: PType): Rope = else: internalError(m.config, "tyStatic for getSimpleTypeDesc") of tyGenericInst, tyAlias, tySink, tyOwned: result = getSimpleTypeDesc(m, lastSon typ) - else: result = nil + else: result = "" - if result != nil and typ.isImportedType(): + if result != "" and typ.isImportedType(): let sig = hashType typ - if cacheGetType(m.typeCache, sig) == nil: + if cacheGetType(m.typeCache, sig) == "": m.typeCache[sig] = result proc pushType(m: BModule, typ: PType) = @@ -325,7 +319,7 @@ proc getTypePre(m: BModule, typ: PType; sig: SigHash): Rope = if typ == nil: result = rope("void") else: result = getSimpleTypeDesc(m, typ) - if result == nil: result = cacheGetType(m.typeCache, sig) + if result == "": result = cacheGetType(m.typeCache, sig) proc structOrUnion(t: PType): Rope = let cachedUnion = rope("union") @@ -346,9 +340,9 @@ proc seqStar(m: BModule): string = proc getTypeForward(m: BModule, typ: PType; sig: SigHash): Rope = result = cacheGetType(m.forwTypeCache, sig) - if result != nil: return + if result != "": return result = getTypePre(m, typ, sig) - if result != nil: return + if result != "": return let concrete = typ.skipTypes(abstractInst) case concrete.kind of tySequence, tyTuple, tyObject: @@ -380,7 +374,7 @@ proc getTypeDescWeak(m: BModule; t: PType; check: var IntSet; kind: TSymKind): R internalError(m.config, "cannot map the empty seq type to a C type") result = cacheGetType(m.forwTypeCache, sig) - if result == nil: + if result == "": result = getTypeName(m, t, sig) if not isImportedType(t): m.forwTypeCache[sig] = result @@ -388,7 +382,7 @@ proc getTypeDescWeak(m: BModule; t: PType; check: var IntSet; kind: TSymKind): R let payload = result & "_Content" addForwardStructFormat(m, rope"struct", payload) - if cacheGetType(m.typeCache, sig) == nil: + if cacheGetType(m.typeCache, sig) == "": m.typeCache[sig] = result #echo "adding ", sig, " ", typeToString(t), " ", m.module.name.s appcg(m, m.s[cfsTypes], @@ -409,7 +403,7 @@ proc getSeqPayloadType(m: BModule; t: PType): Rope = proc seqV2ContentType(m: BModule; t: PType; check: var IntSet) = let sig = hashType(t) let result = cacheGetType(m.typeCache, sig) - if result == nil: + if result == "": discard getTypeDescAux(m, t, check, skVar) else: # little hack for now to prevent multiple definitions of the same @@ -431,30 +425,31 @@ proc paramStorageLoc(param: PSym): TStorageLoc = proc genProcParams(m: BModule, t: PType, rettype, params: var Rope, check: var IntSet, declareEnvironment=true; weakDep=false) = - params = nil + params = "" if t[0] == nil or isInvalidReturnType(m.config, t): - rettype = ~"void" + rettype = "void" else: rettype = getTypeDescAux(m, t[0], check, skResult) for i in 1.. 1: result.add(" COMMA ") addResultType(origTyp[i]) @@ -875,13 +872,13 @@ proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet; kind: TSymKin # The resulting type will include commas and these won't play well # with the C macros for defining procs such as N_NIMCALL. We must # create a typedef for the type and use it in the proc signature: - let typedefName = ~"TY" & $sig + let typedefName = "TY" & $sig m.s[cfsTypes].addf("typedef $1 $2;$n", [result, typedefName]) m.typeCache[sig] = typedefName result = typedefName else: result = cacheGetType(m.forwTypeCache, sig) - if result == nil: + if result == "": result = getTypeName(m, origTyp, sig) m.forwTypeCache[sig] = result if not isImportedType(t): @@ -897,7 +894,9 @@ proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet; kind: TSymKin discard # addAbiCheck(m, t, result) # already handled elsewhere of tySet: # Don't use the imported name as it may be scoped: 'Foo::SomeKind' - result = $t.kind & '_' & t.lastSon.typeName & $t.lastSon.hashType + result = rope("tySet_") + t.lastSon.typeName(result) + result.add $t.lastSon.hashType m.typeCache[sig] = result if not isImportedType(t): let s = int(getSize(m.config, t)) @@ -910,7 +909,7 @@ proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet; kind: TSymKin result = getTypeDescAux(m, lastSon(t), check, kind) else: internalError(m.config, "getTypeDescAux(" & $t.kind & ')') - result = nil + result = "" # fixes bug #145: excl(check, t.id) @@ -961,21 +960,12 @@ proc isReloadable(m: BModule, prc: PSym): bool = proc isNonReloadable(m: BModule, prc: PSym): bool = return m.hcrOn and sfNonReloadable in prc.flags -proc genProcHeader(m: BModule, prc: PSym, asPtr: bool = false): Rope = - var - rettype, params: Rope +proc genProcHeader(m: BModule, prc: PSym; result: var Rope; asPtr: bool = false) = # using static is needed for inline procs - if lfExportLib in prc.loc.flags: - if isHeaderFile in m.flags: - result.add "N_LIB_IMPORT " - else: - result.add "N_LIB_EXPORT " - elif prc.typ.callConv == ccInline or asPtr or isNonReloadable(m, prc): - result.add "static " - elif sfImportc notin prc.flags: - result.add "N_LIB_PRIVATE " var check = initIntSet() - fillLoc(prc.loc, locProc, prc.ast[namePos], mangleName(m, prc), OnUnknown) + fillBackendName(m, prc) + fillLoc(prc.loc, locProc, prc.ast[namePos], OnUnknown) + var rettype, params: Rope genProcParams(m, prc.typ, rettype, params, check) # handle the 2 options for hotcodereloading codegen - function pointer # (instead of forward declaration) or header for function body with "_actual" postfix @@ -986,12 +976,21 @@ proc genProcHeader(m: BModule, prc: PSym, asPtr: bool = false): Rope = # careful here! don't access ``prc.ast`` as that could reload large parts of # the object graph! if prc.constraint.isNil: + if lfExportLib in prc.loc.flags: + if isHeaderFile in m.flags: + result.add "N_LIB_IMPORT " + else: + result.add "N_LIB_EXPORT " + elif prc.typ.callConv == ccInline or asPtr or isNonReloadable(m, prc): + result.add "static " + elif sfImportc notin prc.flags: + result.add "N_LIB_PRIVATE " result.addf("$1$2($3, $4)$5", [rope(CallingConvToStr[prc.typ.callConv]), asPtrStr, rettype, name, params]) else: let asPtrStr = if asPtr: (rope("(*") & name & ")") else: name - result = runtimeFormat(prc.cgDeclFrmt, [rettype, asPtrStr, params]) + result.add runtimeFormat(prc.cgDeclFrmt, [rettype, asPtrStr, params]) # ------------------ type info generation ------------------------------------- @@ -1030,7 +1029,7 @@ proc genTypeInfoAuxBase(m: BModule; typ, origType: PType; #else echo("can contain a cycle: " & typeToString(typ)) if flags != 0: m.s[cfsTypeInit3].addf("$1.flags = $2;$n", [nameHcr, rope(flags)]) - discard cgsym(m, "TNimType") + cgsym(m, "TNimType") if isDefined(m.config, "nimTypeNames"): var typename = typeToString(if origType.typeInst != nil: origType.typeInst else: origType, preferName) @@ -1038,16 +1037,16 @@ proc genTypeInfoAuxBase(m: BModule; typ, origType: PType; typename = "anon ref object from " & m.config$origType.skipTypes(skipPtrs).sym.info m.s[cfsTypeInit3].addf("$1.name = $2;$n", [nameHcr, makeCString typename]) - discard cgsym(m, "nimTypeRoot") + cgsym(m, "nimTypeRoot") m.s[cfsTypeInit3].addf("$1.nextType = nimTypeRoot; nimTypeRoot=&$1;$n", [nameHcr]) if m.hcrOn: - m.s[cfsData].addf("static TNimType* $1;$n", [name]) + m.s[cfsStrData].addf("static TNimType* $1;$n", [name]) m.hcrCreateTypeInfosProc.addf("\thcrRegisterGlobal($2, \"$1\", sizeof(TNimType), NULL, (void**)&$1);$n", [name, getModuleDllPath(m, m.module)]) else: - m.s[cfsData].addf("N_LIB_PRIVATE TNimType $1;$n", [name]) + m.s[cfsStrData].addf("N_LIB_PRIVATE TNimType $1;$n", [name]) proc genTypeInfoAux(m: BModule, typ, origType: PType, name: Rope; info: TLineInfo) = @@ -1075,7 +1074,7 @@ proc discriminatorTableName(m: BModule, objtype: PType, d: PSym): Rope = proc rope(arg: Int128): Rope = rope($arg) proc discriminatorTableDecl(m: BModule, objtype: PType, d: PSym): Rope = - discard cgsym(m, "TNimNode") + cgsym(m, "TNimNode") var tmp = discriminatorTableName(m, objtype, d) result = "TNimNode* $1[$2];$n" % [tmp, rope(lengthOrd(m.config, d.typ)+1)] @@ -1110,7 +1109,7 @@ proc genObjectFields(m: BModule, typ, origType: PType, n: PNode, expr: Rope; var tmp = discriminatorTableName(m, typ, field) var L = lengthOrd(m.config, field.typ) assert L > 0 - if field.loc.r == nil: fillObjectFields(m, typ) + if field.loc.r == "": fillObjectFields(m, typ) if field.loc.t == nil: internalError(m.config, n.info, "genObjectFields") m.s[cfsTypeInit3].addf("$1.kind = 3;$n" & @@ -1148,7 +1147,7 @@ proc genObjectFields(m: BModule, typ, origType: PType, n: PNode, expr: Rope; # Do not produce code for void types if isEmptyType(field.typ): return if field.bitsize == 0: - if field.loc.r == nil: fillObjectFields(m, typ) + if field.loc.r == "": fillObjectFields(m, typ) if field.loc.t == nil: internalError(m.config, n.info, "genObjectFields") m.s[cfsTypeInit3].addf("$1.kind = 1;$n" & @@ -1242,7 +1241,7 @@ proc genSetInfo(m: BModule, typ: PType, name: Rope; info: TLineInfo) = assert(typ[0] != nil) genTypeInfoAux(m, typ, typ, name, info) var tmp = getNimNode(m) - m.s[cfsTypeInit3].addf("$1.len = $2; $1.kind = 0;$n" & "$3.node = &$1;$n", + m.s[cfsTypeInit3].addf("$1.len = $2; $1.kind = 0;$n$3.node = &$1;$n", [tmp, rope(firstOrd(m.config, typ)), tiNameForHcr(m, name)]) proc genArrayInfo(m: BModule, typ: PType, name: Rope; info: TLineInfo) = @@ -1267,18 +1266,18 @@ proc genDeepCopyProc(m: BModule; s: PSym; result: Rope) = proc declareNimType(m: BModule, name: string; str: Rope, module: int) = let nr = rope(name) if m.hcrOn: - m.s[cfsData].addf("static $2* $1;$n", [str, nr]) + m.s[cfsStrData].addf("static $2* $1;$n", [str, nr]) m.s[cfsTypeInit1].addf("\t$1 = ($3*)hcrGetGlobal($2, \"$1\");$n", [str, getModuleDllPath(m, module), nr]) else: - m.s[cfsData].addf("extern $2 $1;$n", [str, nr]) + m.s[cfsStrData].addf("extern $2 $1;$n", [str, nr]) proc genTypeInfo2Name(m: BModule; t: PType): Rope = var res = "|" var it = t while it != nil: it = it.skipTypes(skipPtrs) - if it.sym != nil: + if it.sym != nil and tfFromGeneric notin it.flags: var m = it.sym.owner while m != nil and m.kind != skModule: m = m.owner if m == nil or sfSystemModule in m.flags: @@ -1298,7 +1297,7 @@ proc genTypeInfo2Name(m: BModule; t: PType): Rope = proc isTrivialProc(g: ModuleGraph; s: PSym): bool {.inline.} = getBody(g, s).len == 0 -proc genHook(m: BModule; t: PType; info: TLineInfo; op: TTypeAttachedOp): Rope = +proc genHook(m: BModule; t: PType; info: TLineInfo; op: TTypeAttachedOp; result: var Rope) = let theProc = getAttachedOp(m.g.graph, t, op) if theProc != nil and not isTrivialProc(m.g.graph, theProc): # the prototype of a destructor is ``=destroy(x: var T)`` and that of a @@ -1309,7 +1308,7 @@ proc genHook(m: BModule; t: PType; info: TLineInfo; op: TTypeAttachedOp): Rope = theProc.name.s & " needs to have the 'nimcall' calling convention") genProc(m, theProc) - result = theProc.loc.r + result.add theProc.loc.r when false: if not canFormAcycle(t) and op == attachedTrace: @@ -1321,7 +1320,7 @@ proc genHook(m: BModule; t: PType; info: TLineInfo; op: TTypeAttachedOp): Rope = # unfortunately this check is wrong for an object type that only contains # .cursor fields like 'Node' inside 'cycleleak'. internalError(m.config, info, "no attached trace proc found") - result = rope("NIM_NIL") + result.add rope("NIM_NIL") proc genTypeInfoV2Impl(m: BModule, t, origType: PType, name: Rope; info: TLineInfo) = var typeName: Rope @@ -1333,17 +1332,23 @@ proc genTypeInfoV2Impl(m: BModule, t, origType: PType, name: Rope; info: TLineIn else: typeName = rope("NIM_NIL") - discard cgsym(m, "TNimTypeV2") - m.s[cfsData].addf("N_LIB_PRIVATE TNimTypeV2 $1;$n", [name]) - let destroyImpl = genHook(m, t, info, attachedDestructor) - let traceImpl = genHook(m, t, info, attachedTrace) + cgsym(m, "TNimTypeV2") + m.s[cfsStrData].addf("N_LIB_PRIVATE TNimTypeV2 $1;$n", [name]) var flags = 0 if not canFormAcycle(t): flags = flags or 1 - addf(m.s[cfsTypeInit3], "$1.destructor = (void*)$2; $1.size = sizeof($3); $1.align = NIM_ALIGNOF($3); $1.name = $4;$n; $1.traceImpl = (void*)$5; $1.flags = $6;", [ - name, destroyImpl, getTypeDesc(m, t), typeName, - traceImpl, rope(flags)]) + var typeEntry = newRopeAppender() + addf(typeEntry, "$1.destructor = (void*)", [name]) + genHook(m, t, info, attachedDestructor, typeEntry) + + addf(typeEntry, "; $1.traceImpl = (void*)", [name]) + genHook(m, t, info, attachedTrace, typeEntry) + + addf(typeEntry, "; $1.name = $2;$n; $1.size = sizeof($3); $1.align = NIM_ALIGNOF($3); $1.flags = $4;", + [name, typeName, getTypeDesc(m, t), rope(flags)]) + + m.s[cfsTypeInit3].add typeEntry if t.kind == tyObject and t.len > 0 and t[0] != nil and optEnableDeepCopy in m.config.globalOptions: discard genTypeInfoV1(m, t, info) @@ -1357,12 +1362,12 @@ proc genTypeInfoV2(m: BModule, t: PType; info: TLineInfo): Rope = let sig = hashType(origType) result = m.typeInfoMarkerV2.getOrDefault(sig) - if result != nil: + if result != "": return prefixTI.rope & result & ")".rope let marker = m.g.typeInfoMarkerV2.getOrDefault(sig) - if marker.str != nil: - discard cgsym(m, "TNimTypeV2") + if marker.str != "": + cgsym(m, "TNimTypeV2") declareNimType(m, "TNimTypeV2", marker.str, marker.owner) # also store in local type section: m.typeInfoMarkerV2[sig] = marker.str @@ -1376,7 +1381,7 @@ proc genTypeInfoV2(m: BModule, t: PType; info: TLineInfo): Rope = # make sure the type info is created in the owner module discard genTypeInfoV2(m.g.modules[owner], origType, info) # reference the type info as extern here - discard cgsym(m, "TNimTypeV2") + cgsym(m, "TNimTypeV2") declareNimType(m, "TNimTypeV2", result, owner) return prefixTI.rope & result & ")".rope @@ -1428,13 +1433,13 @@ proc genTypeInfoV1(m: BModule, t: PType; info: TLineInfo): Rope = let sig = hashType(origType) result = m.typeInfoMarker.getOrDefault(sig) - if result != nil: + if result != "": return prefixTI.rope & result & ")".rope let marker = m.g.typeInfoMarker.getOrDefault(sig) - if marker.str != nil: - discard cgsym(m, "TNimType") - discard cgsym(m, "TNimNode") + if marker.str != "": + cgsym(m, "TNimType") + cgsym(m, "TNimNode") declareNimType(m, "TNimType", marker.str, marker.owner) # also store in local type section: m.typeInfoMarker[sig] = marker.str @@ -1445,8 +1450,8 @@ proc genTypeInfoV1(m: BModule, t: PType; info: TLineInfo): Rope = let old = m.g.graph.emittedTypeInfo.getOrDefault($result) if old != FileIndex(0): - discard cgsym(m, "TNimType") - discard cgsym(m, "TNimNode") + cgsym(m, "TNimType") + cgsym(m, "TNimNode") declareNimType(m, "TNimType", result, old.int) return prefixTI.rope & result & ")".rope @@ -1455,8 +1460,8 @@ proc genTypeInfoV1(m: BModule, t: PType; info: TLineInfo): Rope = # make sure the type info is created in the owner module discard genTypeInfoV1(m.g.modules[owner], origType, info) # reference the type info as extern here - discard cgsym(m, "TNimType") - discard cgsym(m, "TNimNode") + cgsym(m, "TNimType") + cgsym(m, "TNimNode") declareNimType(m, "TNimType", result, owner) return prefixTI.rope & result & ")".rope else: @@ -1483,12 +1488,12 @@ proc genTypeInfoV1(m: BModule, t: PType; info: TLineInfo): Rope = genTupleInfo(m, x, x, result, info) of tySequence: genTypeInfoAux(m, t, t, result, info) - if m.config.selectedGC in {gcMarkAndSweep, gcRefc, gcV2, gcGo}: + if m.config.selectedGC in {gcMarkAndSweep, gcRefc, gcGo}: let markerProc = genTraverseProc(m, origType, sig) m.s[cfsTypeInit3].addf("$1.marker = $2;$n", [tiNameForHcr(m, result), markerProc]) of tyRef: genTypeInfoAux(m, t, t, result, info) - if m.config.selectedGC in {gcMarkAndSweep, gcRefc, gcV2, gcGo}: + if m.config.selectedGC in {gcMarkAndSweep, gcRefc, gcGo}: let markerProc = genTraverseProc(m, origType, sig) m.s[cfsTypeInit3].addf("$1.marker = $2;$n", [tiNameForHcr(m, result), markerProc]) of tyPtr, tyRange, tyUncheckedArray: genTypeInfoAux(m, t, t, result, info) diff --git a/compiler/cgen.nim b/compiler/cgen.nim index ad59b759fe19..794abc1ad6a5 100644 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -15,8 +15,7 @@ import ccgutils, os, ropes, math, passes, wordrecg, treetab, cgmeth, rodutils, renderer, cgendata, aliases, lowerings, tables, sets, ndi, lineinfos, pathutils, transf, - injectdestructors, astmsgs - + injectdestructors, astmsgs, modulepaths when defined(nimPreviewSlimSystem): import std/assertions @@ -24,7 +23,7 @@ when defined(nimPreviewSlimSystem): when not defined(leanCompiler): import spawn, semparallel -import strutils except `%` # collides with ropes.`%` +import strutils except `%`, addf # collides with ropes.`%` from ic / ic import ModuleBackendFlag import dynlib @@ -64,16 +63,23 @@ proc initLoc(result: var TLoc, k: TLocKind, lode: PNode, s: TStorageLoc) = result.k = k result.storage = s result.lode = lode - result.r = nil + result.r = "" result.flags = {} -proc fillLoc(a: var TLoc, k: TLocKind, lode: PNode, r: Rope, s: TStorageLoc) = +proc fillLoc(a: var TLoc, k: TLocKind, lode: PNode, r: Rope, s: TStorageLoc) {.inline.} = + # fills the loc if it is not already initialized + if a.k == locNone: + a.k = k + a.lode = lode + a.storage = s + if a.r == "": a.r = r + +proc fillLoc(a: var TLoc, k: TLocKind, lode: PNode, s: TStorageLoc) {.inline.} = # fills the loc if it is not already initialized if a.k == locNone: a.k = k a.lode = lode a.storage = s - if a.r == nil: a.r = r proc t(a: TLoc): PType {.inline.} = if a.lode.kind == nkSym: @@ -97,7 +103,8 @@ proc useHeader(m: BModule, sym: PSym) = let str = getStr(sym.annex.path) m.includeHeader(str) -proc cgsym(m: BModule, name: string): Rope +proc cgsym(m: BModule, name: string) +proc cgsymValue(m: BModule, name: string): Rope proc getCFile(m: BModule): AbsoluteFile @@ -114,10 +121,6 @@ proc getModuleDllPath(m: BModule, s: PSym): Rope = import macros -proc cgFormatValue(result: var string; value: Rope) = - for str in leaves(value): - result.add str - proc cgFormatValue(result: var string; value: string) = result.add value @@ -198,7 +201,7 @@ macro ropecg(m: BModule, frmt: static[FormatStr], args: untyped): Rope = var ident = newLit(substr(frmt, i, j-1)) i = j flushStrLit() - result.add newCall(formatValue, resVar, newCall(ident"cgsym", m, ident)) + result.add newCall(formatValue, resVar, newCall(ident"cgsymValue", m, ident)) elif frmt[i] == '#' and frmt[i+1] == '$': inc(i, 2) var j = 0 @@ -207,7 +210,7 @@ macro ropecg(m: BModule, frmt: static[FormatStr], args: untyped): Rope = inc(i) let ident = args[j-1] flushStrLit() - result.add newCall(formatValue, resVar, newCall(ident"cgsym", m, ident)) + result.add newCall(formatValue, resVar, newCall(ident"cgsymValue", m, ident)) var start = i while i < frmt.len: if frmt[i] != '$' and frmt[i] != '#': inc(i) @@ -218,10 +221,9 @@ macro ropecg(m: BModule, frmt: static[FormatStr], args: untyped): Rope = flushStrLit() result.add newCall(ident"rope", resVar) -proc indentLine(p: BProc, r: Rope): Rope = - result = r +proc addIndent(p: BProc; result: var Rope) = for i in 0.. 0: result.addf("NIM_ALIGN($1) ", [rope(s.alignment)]) @@ -562,7 +581,8 @@ proc treatGlobalDifferentlyForHCR(m: BModule, s: PSym): bool = proc assignGlobalVar(p: BProc, n: PNode; value: Rope) = let s = n.sym if s.loc.k == locNone: - fillLoc(s.loc, locGlobalVar, n, mangleName(p.module, s), OnHeap) + fillBackendName(p.module, s) + fillLoc(s.loc, locGlobalVar, n, OnHeap) if treatGlobalDifferentlyForHCR(p.module, s): incl(s.loc.flags, lfIndirect) if lfDynamicLib in s.loc.flags: @@ -571,7 +591,7 @@ proc assignGlobalVar(p: BProc, n: PNode; value: Rope) = varInDynamicLib(q, s) else: s.loc.r = mangleDynLibProc(s) - if value != nil: + if value != "": internalError(p.config, n.info, ".dynlib variables cannot have a value") return useHeader(p.module, s) @@ -579,10 +599,10 @@ proc assignGlobalVar(p: BProc, n: PNode; value: Rope) = if not containsOrIncl(p.module.declaredThings, s.id): if sfThread in s.flags: declareThreadVar(p.module, s, sfImportc in s.flags) - if value != nil: + if value != "": internalError(p.config, n.info, ".threadvar variables cannot have a value") else: - var decl: Rope = nil + var decl: Rope = "" var td = getTypeDesc(p.module, s.loc.t, skVar) if s.constraint.isNil: if s.kind in {skLet, skVar, skField, skForVar} and s.alignment > 0: @@ -591,34 +611,35 @@ proc assignGlobalVar(p: BProc, n: PNode; value: Rope) = elif sfImportc in s.flags: decl.add("extern ") elif lfExportLib in s.loc.flags: decl.add("N_LIB_EXPORT_VAR ") else: decl.add("N_LIB_PRIVATE ") - if s.kind == skLet and value != nil: decl.add("NIM_CONST ") + if s.kind == skLet and value != "": decl.add("NIM_CONST ") decl.add(td) if p.hcrOn: decl.add("*") if sfRegister in s.flags: decl.add(" register") if sfVolatile in s.flags: decl.add(" volatile") if sfNoalias in s.flags: decl.add(" NIM_NOALIAS") - if value != nil: + if value != "": decl.addf(" $1 = $2;$n", [s.loc.r, value]) else: decl.addf(" $1;$n", [s.loc.r]) else: - if value != nil: + if value != "": decl = runtimeFormat(s.cgDeclFrmt & " = $#;$n", [td, s.loc.r, value]) else: decl = runtimeFormat(s.cgDeclFrmt & ";$n", [td, s.loc.r]) p.module.s[cfsVars].add(decl) - if p.withinLoop > 0 and value == nil: + if p.withinLoop > 0 and value == "": # fixes tests/run/tzeroarray: resetLoc(p, s.loc) proc assignParam(p: BProc, s: PSym, retType: PType) = - assert(s.loc.r != nil) + assert(s.loc.r != "") scopeMangledParam(p, s) proc fillProcLoc(m: BModule; n: PNode) = let sym = n.sym if sym.loc.k == locNone: - fillLoc(sym.loc, locProc, n, mangleName(m, sym), OnStack) + fillBackendName(m, sym) + fillLoc(sym.loc, locProc, n, OnStack) proc getLabel(p: BProc): TLabel = inc(p.labels) @@ -633,9 +654,9 @@ proc genStmts(p: BProc, t: PNode) proc expr(p: BProc, n: PNode, d: var TLoc) proc genProcPrototype(m: BModule, sym: PSym) proc putLocIntoDest(p: BProc, d: var TLoc, s: TLoc) -proc intLiteral(i: BiggestInt): Rope -proc genLiteral(p: BProc, n: PNode): Rope -proc genOtherArg(p: BProc; ri: PNode; i: int; typ: PType): Rope +proc intLiteral(i: BiggestInt; result: var Rope) +proc genLiteral(p: BProc, n: PNode; result: var Rope) +proc genOtherArg(p: BProc; ri: PNode; i: int; typ: PType; result: var Rope; argsCounter: var int) proc raiseExit(p: BProc) proc initLocExpr(p: BProc, e: PNode, result: var TLoc) = @@ -673,11 +694,11 @@ proc initFrame(p: BProc, procname, filename: Rope): Rope = if p.module.s[cfsFrameDefines].len == 0: appcg(p.module, p.module.s[cfsFrameDefines], frameDefines, ["#"]) - discard cgsym(p.module, "nimFrame") + cgsym(p.module, "nimFrame") result = ropecg(p.module, "\tnimfr_($1, $2);$n", [procname, filename]) proc initFrameNoDebug(p: BProc; frame, procname, filename: Rope; line: int): Rope = - discard cgsym(p.module, "nimFrame") + cgsym(p.module, "nimFrame") p.blocks[0].sections[cpsLocals].addf("TFrame $1;$n", [frame]) result = ropecg(p.module, "\t$1.procname = $2; $1.filename = $3; " & " $1.line = $4; $1.len = -1; nimFrame(&$1);$n", @@ -704,24 +725,28 @@ proc loadDynamicLib(m: BModule, lib: PLib) = if not lib.generated: lib.generated = true var tmp = getTempName(m) - assert(lib.name == nil) + assert(lib.name == "") lib.name = tmp # BUGFIX: cgsym has awful side-effects m.s[cfsVars].addf("static void* $1;$n", [tmp]) if lib.path.kind in {nkStrLit..nkTripleStrLit}: var s: TStringSeq = @[] libCandidates(lib.path.strVal, s) rawMessage(m.config, hintDependency, lib.path.strVal) - var loadlib: Rope = nil + var loadlib: Rope = "" for i in 0..high(s): inc(m.labels) if i > 0: loadlib.add("||") let n = newStrNode(nkStrLit, s[i]) n.info = lib.path.info - appcg(m, loadlib, "($1 = #nimLoadLibrary($2))$n", - [tmp, genStringLiteral(m, n)]) + appcg(m, loadlib, "($1 = #nimLoadLibrary(", [tmp]) + genStringLiteral(m, n, loadlib) + loadlib.addf "))$n", [] appcg(m, m.s[cfsDynLibInit], - "if (!($1)) #nimLoadLibraryError($2);$n", - [loadlib, genStringLiteral(m, lib.path)]) + "if (!($1)) #nimLoadLibraryError(", + [loadlib]) + genStringLiteral(m, lib.path, m.s[cfsDynLibInit]) + m.s[cfsDynLibInit].addf ");$n", [] + else: var p = newProc(nil, m) p.options.excl optStackTrace @@ -740,7 +765,7 @@ proc loadDynamicLib(m: BModule, lib: PLib) = "if (!($1 = #nimLoadLibrary($2))) #nimLoadLibraryError($2);$n", [tmp, rdLoc(dest)]) - if lib.name == nil: internalError(m.config, "loadDynamicLib") + if lib.name == "": internalError(m.config, "loadDynamicLib") proc mangleDynLibProc(sym: PSym): Rope = # we have to build this as a single rope in order not to trip the @@ -805,18 +830,25 @@ proc symInDynamicLibPartial(m: BModule, sym: PSym) = sym.loc.r = mangleDynLibProc(sym) sym.typ.sym = nil # generate a new name -proc cgsym(m: BModule, name: string): Rope = +proc cgsymImpl(m: BModule; sym: PSym) {.inline.} = + case sym.kind + of skProc, skFunc, skMethod, skConverter, skIterator: genProc(m, sym) + of skVar, skResult, skLet: genVarPrototype(m, newSymNode sym) + of skType: discard getTypeDesc(m, sym.typ) + else: internalError(m.config, "cgsym: " & $sym.kind) + +proc cgsym(m: BModule, name: string) = + let sym = magicsys.getCompilerProc(m.g.graph, name) + if sym != nil: + cgsymImpl m, sym + else: + rawMessage(m.config, errGenerated, "system module needs: " & name) + +proc cgsymValue(m: BModule, name: string): Rope = let sym = magicsys.getCompilerProc(m.g.graph, name) if sym != nil: - case sym.kind - of skProc, skFunc, skMethod, skConverter, skIterator: genProc(m, sym) - of skVar, skResult, skLet: genVarPrototype(m, newSymNode sym) - of skType: discard getTypeDesc(m, sym.typ) - else: internalError(m.config, "cgsym: " & name & ": " & $sym.kind) + cgsymImpl m, sym else: - # we used to exclude the system module from this check, but for DLL - # generation support this sloppyness leads to hard to detect bugs, so - # we're picky here for the system module too: rawMessage(m.config, errGenerated, "system module needs: " & name) result = sym.loc.r if m.hcrOn and sym != nil and sym.kind in {skProc..skIterator}: @@ -847,12 +879,12 @@ proc generateHeaders(m: BModule) = #undef unix """) -proc openNamespaceNim(namespace: string): Rope = +proc openNamespaceNim(namespace: string; result: var Rope) = result.add("namespace ") result.add(namespace) result.add(" {\L") -proc closeNamespaceNim(): Rope = +proc closeNamespaceNim(result: var Rope) = result.add("}\L") proc closureSetup(p: BProc, prc: PSym) = @@ -967,7 +999,7 @@ proc allPathsAsgnResult(n: PNode): InitResultEnum = if containsResult(n[0]): return InitRequired result = InitSkippable var exhaustive = skipTypes(n[0].typ, - abstractVarRange-{tyTypeDesc}).kind notin {tyFloat..tyFloat128, tyString} + abstractVarRange-{tyTypeDesc}).kind notin {tyFloat..tyFloat128, tyString, tyCstring} for i in 1.. 0: - m.s[cfsProcs].add closeNamespaceNim() & "using namespace " & m.config.cppCustomNamespace & ";\L" + closeNamespaceNim(m.s[cfsProcs]) + m.s[cfsProcs].add "using namespace " & m.config.cppCustomNamespace & ";\L" if m.config.target.targetOS == osWindows and m.config.globalOptions * {optGenGuiApp, optGenDynLib} != {}: @@ -1550,16 +1590,16 @@ proc genMainProc(m: BModule) = appcg(m, m.s[cfsProcs], otherMain, [if m.hcrOn: "*" else: "", m.config.nimMainPrefix]) if m.config.cppCustomNamespace.len > 0: - m.s[cfsProcs].add openNamespaceNim(m.config.cppCustomNamespace) + openNamespaceNim(m.config.cppCustomNamespace, m.s[cfsProcs]) proc registerInitProcs*(g: BModuleList; m: PSym; flags: set[ModuleBackendFlag]) = ## Called from the IC backend. if HasDatInitProc in flags: - let datInit = getSomeNameForModule(m) & "DatInit000" + let datInit = getSomeNameForModule(g.config, g.config.toFullPath(m.info.fileIndex).AbsoluteFile) & "DatInit000" g.mainModProcs.addf("N_LIB_PRIVATE N_NIMCALL(void, $1)(void);$N", [datInit]) g.mainDatInit.addf("\t$1();$N", [datInit]) if HasModuleInitProc in flags: - let init = getSomeNameForModule(m) & "Init000" + let init = getSomeNameForModule(g.config, g.config.toFullPath(m.info.fileIndex).AbsoluteFile) & "Init000" g.mainModProcs.addf("N_LIB_PRIVATE N_NIMCALL(void, $1)(void);$N", [init]) let initCall = "\t$1();$N" % [init] if sfMainModule in m.flags: @@ -1733,7 +1773,7 @@ proc genInitCode(m: BModule) = # Give this small function its own scope prc.addf("{$N", []) # Keep a bogus frame in case the code needs one - prc.add(~"\tTFrame FR_; FR_.len = 0;$N") + prc.add("\tTFrame FR_; FR_.len = 0;\n") writeSection(preInitProc, cpsLocals) writeSection(preInitProc, cpsInit, m.hcrOn) @@ -1759,13 +1799,13 @@ proc genInitCode(m: BModule) = var procname = makeCString(m.module.name.s) prc.add(initFrame(m.initProc, procname, quotedFilename(m.config, m.module.info))) else: - prc.add(~"\tTFrame FR_; FR_.len = 0;$N") + prc.add("\tTFrame FR_; FR_.len = 0;\n") writeSection(initProc, cpsInit, m.hcrOn) writeSection(initProc, cpsStmts) if beforeRetNeeded in m.initProc.flags: - prc.add(~"\tBeforeRet_: ;$n") + prc.add("\tBeforeRet_: ;\n") if sfMainModule in m.module.flags and m.config.exc == excGoto: if getCompilerProc(m.g.graph, "nimTestErrorFlag") != nil: @@ -1796,7 +1836,7 @@ proc genInitCode(m: BModule) = m.s[cfsInitProc].addf("}$N$N", []) for i, el in pairs(m.extensionLoaders): - if el != nil: + if el != "": let ex = "NIM_EXTERNC N_NIMCALL(void, nimLoadProcs$1)(void) {$2}$N$N" % [(i.ord - '0'.ord).rope, el] moduleInitRequired = true @@ -1824,7 +1864,7 @@ proc genModule(m: BModule, cfile: Cfile): Rope = generateHeaders(m) result.add(m.s[cfsHeaders]) if m.config.cppCustomNamespace.len > 0: - result.add openNamespaceNim(m.config.cppCustomNamespace) + openNamespaceNim(m.config.cppCustomNamespace, result) if m.s[cfsFrameDefines].len > 0: result.add(m.s[cfsFrameDefines]) else: @@ -1843,10 +1883,10 @@ proc genModule(m: BModule, cfile: Cfile): Rope = result.add(m.s[cfsDatInitProc]) if m.config.cppCustomNamespace.len > 0: - result.add closeNamespaceNim() + closeNamespaceNim(result) if moduleIsEmpty: - result = nil + result = "" proc initProcOptions(m: BModule): TOptions = let opts = m.config.options @@ -1867,6 +1907,7 @@ proc rawNewModule(g: BModuleList; module: PSym, filename: AbsoluteFile): BModule result.typeInfoMarker = initTable[SigHash, Rope]() result.sigConflicts = initCountTable[SigHash]() result.initProc = newProc(nil, result) + for i in low(result.s)..high(result.s): result.s[i] = newRopeAppender() result.initProc.options = initProcOptions(result) result.preInitProc = newProc(nil, result) result.preInitProc.flags.incl nimErrorFlagDisabled @@ -1924,13 +1965,14 @@ proc writeHeader(m: BModule) = generateThreadLocalStorage(m) for i in cfsHeaders..cfsProcs: result.add(m.s[i]) - if m.config.cppCustomNamespace.len > 0 and i == cfsHeaders: result.add openNamespaceNim(m.config.cppCustomNamespace) + if m.config.cppCustomNamespace.len > 0 and i == cfsHeaders: + openNamespaceNim(m.config.cppCustomNamespace, result) result.add(m.s[cfsInitProc]) if optGenDynLib in m.config.globalOptions: result.add("N_LIB_IMPORT ") result.addf("N_CDECL(void, $1NimMain)(void);$n", [rope m.config.nimMainPrefix]) - if m.config.cppCustomNamespace.len > 0: result.add closeNamespaceNim() + if m.config.cppCustomNamespace.len > 0: closeNamespaceNim(result) result.addf("#endif /* $1 */$n", [guard]) if not writeRope(result, m.filename): rawMessage(m.config, errCannotOpenFile, m.filename.string) @@ -1940,7 +1982,7 @@ proc getCFile(m: BModule): AbsoluteFile = if m.compileToCpp: ".nim.cpp" elif m.config.backend == backendObjc or sfCompileToObjc in m.module.flags: ".nim.m" else: ".nim.c" - result = changeFileExt(completeCfilePath(m.config, withPackageName(m.config, m.cfilename)), ext) + result = changeFileExt(completeCfilePath(m.config, mangleModuleName(m.config, m.cfilename).AbsoluteFile), ext) when false: proc myOpenCached(graph: ModuleGraph; module: PSym, rd: PRodReader): PPassContext = @@ -2034,7 +2076,7 @@ proc writeModule(m: BModule, pending: bool) = var cf = Cfile(nimname: m.module.name.s, cname: cfile, obj: completeCfilePath(m.config, toObjFile(m.config, cfile)), flags: {}) var code = genModule(m, cf) - if code != nil or m.config.symbolFiles != disabledSf: + if code != "" or m.config.symbolFiles != disabledSf: when hasTinyCBackend: if m.config.cmd == cmdTcc: tccgen.compileCCode($code, m.config) @@ -2060,7 +2102,7 @@ proc finalCodegenActions*(graph: ModuleGraph; m: BModule; n: PNode) = # phase ordering problem here: We need to announce this # dependency to 'nimTestErrorFlag' before system.c has been written to disk. if m.config.exc == excGoto and getCompilerProc(graph, "nimTestErrorFlag") != nil: - discard cgsym(m, "nimTestErrorFlag") + cgsym(m, "nimTestErrorFlag") if {optGenStaticLib, optGenDynLib, optNoMain} * m.config.globalOptions == {}: for i in countdown(high(graph.globalDestructors), 0): @@ -2076,7 +2118,7 @@ proc finalCodegenActions*(graph: ModuleGraph; m: BModule; n: PNode) = if m.hcrOn: # make sure this is pulled in (meaning hcrGetGlobal() is called for it during init) - discard cgsym(m, "programResult") + cgsym(m, "programResult") if m.inHcrInitGuard: endBlock(m.initProc) @@ -2086,17 +2128,17 @@ proc finalCodegenActions*(graph: ModuleGraph; m: BModule; n: PNode) = # so it can load the HCR runtime and later pass the library handle to the HCR runtime which # will in turn pass it to the other modules it initializes so they can initialize the # register/get procs so they don't have to have the definitions of these functions as well - discard cgsym(m, "nimLoadLibrary") - discard cgsym(m, "nimLoadLibraryError") - discard cgsym(m, "nimGetProcAddr") - discard cgsym(m, "procAddrError") - discard cgsym(m, "rawWrite") + cgsym(m, "nimLoadLibrary") + cgsym(m, "nimLoadLibraryError") + cgsym(m, "nimGetProcAddr") + cgsym(m, "procAddrError") + cgsym(m, "rawWrite") # raise dependencies on behalf of genMainProc - if m.config.target.targetOS != osStandalone and m.config.selectedGC != gcNone: - discard cgsym(m, "initStackBottomWith") + if m.config.target.targetOS != osStandalone and m.config.selectedGC notin {gcNone, gcArc, gcOrc}: + cgsym(m, "initStackBottomWith") if emulatedThreadVars(m.config) and m.config.target.targetOS != osStandalone: - discard cgsym(m, "initThreadVarsEmulation") + cgsym(m, "initThreadVarsEmulation") if m.g.forwardedProcs.len == 0: incl m.flags, objHasKidsValid diff --git a/compiler/cgendata.nim b/compiler/cgendata.nim index 4490767254fe..d017fdd1e48f 100644 --- a/compiler/cgendata.nim +++ b/compiler/cgendata.nim @@ -27,6 +27,7 @@ type cfsFieldInfo, # section for field information cfsTypeInfo, # section for type information (ag ABI checks) cfsProcHeaders, # section for C procs prototypes + cfsStrData, # section for constant string literals cfsData, # section for C constant data cfsVars, # section for C variable declarations cfsProcs, # section for C procs that are not inline @@ -190,13 +191,18 @@ proc procSec*(p: BProc, s: TCProcSection): var Rope {.inline.} = # top level proc sections result = p.blocks[0].sections[s] +proc initBlock*(): TBlock = + result = TBlock() + for i in low(result.sections)..high(result.sections): + result.sections[i] = newRopeAppender() + proc newProc*(prc: PSym, module: BModule): BProc = new(result) result.prc = prc result.module = module result.options = if prc != nil: prc.options else: module.config.options - newSeq(result.blocks, 1) + result.blocks = @[initBlock()] result.nestedTryStmts = @[] result.finallySafePoints = @[] result.sigConflicts = initCountTable[string]() diff --git a/compiler/cgmeth.nim b/compiler/cgmeth.nim index dab8826c15ed..6b4322a13f1b 100644 --- a/compiler/cgmeth.nim +++ b/compiler/cgmeth.nim @@ -123,7 +123,7 @@ proc createDispatcher(s: PSym; g: ModuleGraph; idgen: IdGenerator): PSym = if disp.typ.callConv == ccInline: disp.typ.callConv = ccNimCall disp.ast = copyTree(s.ast) disp.ast[bodyPos] = newNodeI(nkEmpty, s.info) - disp.loc.r = nil + disp.loc.r = "" if s.typ[0] != nil: if disp.ast.len > resultPos: disp.ast[resultPos].sym = copySym(s.ast[resultPos].sym, nextSymId(idgen)) diff --git a/compiler/closureiters.nim b/compiler/closureiters.nim index 6370d0dcb1a9..613fbe582c56 100644 --- a/compiler/closureiters.nim +++ b/compiler/closureiters.nim @@ -121,7 +121,10 @@ # yield 2 # if :unrollFinally: # This node is created by `newEndFinallyNode` # if :curExc.isNil: -# return :tmpResult +# if nearestFinally == 0: +# return :tmpResult +# else: +# :state = nearestFinally # bubble up # else: # closureIterSetupExc(nil) # raise @@ -807,7 +810,10 @@ proc newEndFinallyNode(ctx: var Ctx, info: TLineInfo): PNode = # Generate the following code: # if :unrollFinally: # if :curExc.isNil: - # return :tmpResult + # if nearestFinally == 0: + # return :tmpResult + # else: + # :state = nearestFinally # bubble up # else: # raise let curExc = ctx.newCurExcAccess() @@ -816,11 +822,20 @@ proc newEndFinallyNode(ctx: var Ctx, info: TLineInfo): PNode = let cmp = newTree(nkCall, newSymNode(ctx.g.getSysMagic(info, "==", mEqRef), info), curExc, nilnode) cmp.typ = ctx.g.getSysType(info, tyBool) - let asgn = newTree(nkFastAsgn, - newSymNode(getClosureIterResult(ctx.g, ctx.fn, ctx.idgen), info), - ctx.newTmpResultAccess()) + let retStmt = + if ctx.nearestFinally == 0: + # last finally, we can return + let retValue = if ctx.fn.typ[0].isNil: + ctx.g.emptyNode + else: + newTree(nkFastAsgn, + newSymNode(getClosureIterResult(ctx.g, ctx.fn, ctx.idgen), info), + ctx.newTmpResultAccess()) + newTree(nkReturnStmt, retValue) + else: + # bubble up to next finally + newTree(nkGotoState, ctx.g.newIntLit(info, ctx.nearestFinally)) - let retStmt = newTree(nkReturnStmt, asgn) let branch = newTree(nkElifBranch, cmp, retStmt) let nullifyExc = newTree(nkCall, newSymNode(ctx.g.getCompilerProc("closureIterSetupExc")), nilnode) @@ -864,6 +879,13 @@ proc transformReturnsInTry(ctx: var Ctx, n: PNode): PNode = of nkSkip: discard + of nkTryStmt: + if n.hasYields: + # the inner try will handle these transformations + discard + else: + for i in 0..= 0: options.setConfigVar(conf, switch, arg) diff --git a/compiler/condsyms.nim b/compiler/condsyms.nim index 5a7e78d5f8b2..332a802ab8a5 100644 --- a/compiler/condsyms.nim +++ b/compiler/condsyms.nim @@ -70,7 +70,7 @@ proc initDefines*(symbols: StringTableRef) = defineSymbol("nimVmExportFixed") # deadcode defineSymbol("nimHasSymOwnerInMacro") # deadcode defineSymbol("nimNewRuntime") # deadcode - defineSymbol("nimIncrSeqV3") # xxx: turn this into deadcode + defineSymbol("nimIncrSeqV3") # deadcode defineSymbol("nimAshr") # deadcode defineSymbol("nimNoNilSeqs") # deadcode defineSymbol("nimNoNilSeqs2") # deadcode @@ -140,3 +140,6 @@ proc initDefines*(symbols: StringTableRef) = defineSymbol("nimHasEffectsOf") defineSymbol("nimHasEnforceNoRaises") + defineSymbol("nimHasTopDownInference") + defineSymbol("nimHasTemplateRedefinitionPragma") + defineSymbol("nimHasCstringCase") diff --git a/compiler/debugutils.nim b/compiler/debugutils.nim index d109d2121ba6..adbb0517fb0d 100644 --- a/compiler/debugutils.nim +++ b/compiler/debugutils.nim @@ -54,3 +54,19 @@ proc isCompilerDebug*(): bool = {.undef(nimCompilerDebug).} echo 'x' conf0.isDefined("nimCompilerDebug") + +proc enteringDebugSection*() {.exportc, dynlib.} = + ## Provides a way for native debuggers to enable breakpoints, watchpoints, etc + ## when code of interest is being compiled. + ## + ## Set your debugger to break on entering `nimCompilerIsEnteringDebugSection` + ## and then execute a desired command. + discard + +proc exitingDebugSection*() {.exportc, dynlib.} = + ## Provides a way for native debuggers to disable breakpoints, watchpoints, etc + ## when code of interest is no longer being compiled. + ## + ## Set your debugger to break on entering `exitingDebugSection` + ## and then execute a desired command. + discard diff --git a/compiler/depends.nim b/compiler/depends.nim index 30fc961c52dc..93f34dc7c7b8 100644 --- a/compiler/depends.nim +++ b/compiler/depends.nim @@ -9,10 +9,17 @@ # This module implements a dependency file generator. -import - options, ast, ropes, idents, passes, modulepaths, pathutils +import options, ast, ropes, passes, pathutils, msgs, lineinfos + +import modulegraphs + +import std/[os, parseutils] +import strutils except addf +import std/private/globs + +when defined(nimPreviewSlimSystem): + import std/assertions -from modulegraphs import ModuleGraph, PPassContext type TGen = object of PPassContext @@ -28,6 +35,50 @@ proc addDependencyAux(b: Backend; importing, imported: string) = b.dotGraph.addf("\"$1\" -> \"$2\";$n", [rope(importing), rope(imported)]) # s1 -> s2_4[label="[0-9]"]; +proc toNimblePath(s: string, isStdlib: bool): string = + const stdPrefix = "std/" + const pkgPrefix = "pkg/" + if isStdlib: + let sub = "lib/" + var start = s.find(sub) + if start < 0: + doAssert false + else: + start += sub.len + let base = s[start..^1] + + if base.startsWith("system") or base.startsWith("std"): + result = base + else: + for dir in stdlibDirs: + if base.startsWith(dir): + return stdPrefix & base.splitFile.name + + result = stdPrefix & base + else: + var sub = getEnv("NIMBLE_DIR") + if sub.len == 0: + sub = ".nimble/pkgs/" + else: + sub.add "/pkgs/" + var start = s.find(sub) + if start < 0: + result = s + else: + start += sub.len + start += skipUntil(s, '/', start) + start += 1 + result = pkgPrefix & s[start..^1] + +proc addDependency(c: PPassContext, g: PGen, b: Backend, n: PNode) = + doAssert n.kind == nkSym, $n.kind + + let path = splitFile(toProjPath(g.config, n.sym.position.FileIndex)) + let modulePath = splitFile(toProjPath(g.config, g.module.position.FileIndex)) + let parent = nativeToUnixPath(modulePath.dir / modulePath.name).toNimblePath(belongsToStdlib(g.graph, g.module)) + let child = nativeToUnixPath(path.dir / path.name).toNimblePath(belongsToStdlib(g.graph, n.sym)) + addDependencyAux(b, parent, child) + proc addDotDependency(c: PPassContext, n: PNode): PNode = result = n let g = PGen(c) @@ -35,11 +86,9 @@ proc addDotDependency(c: PPassContext, n: PNode): PNode = case n.kind of nkImportStmt: for i in 0.. 0: return -proc belongsToPackage(conf: ConfigRef; module: PSym): bool = - result = module.kind == skModule and module.getnimblePkgId == conf.mainPackageId - proc externalDep(d: PDoc; module: PSym): string = if optWholeProject in d.conf.globalOptions or d.conf.docRoot.len > 0: let full = AbsoluteFile toFullPath(d.conf, FileIndex module.position) @@ -471,7 +480,7 @@ proc nodeToHighlightedHtml(d: PDoc; n: PNode; result: var string; "\\spanIdentifier{$1}", [escLit, procLink]) elif s != nil and s.kind in {skType, skVar, skLet, skConst} and sfExported in s.flags and s.owner != nil and - belongsToPackage(d.conf, s.owner) and d.target == outHtml: + belongsToProjectPackage(d.conf, s.owner) and d.target == outHtml: let external = externalDep(d, s.owner) result.addf "$3", [changeFileExt(external, "html"), literal, @@ -1115,6 +1124,46 @@ proc genJsonItem(d: PDoc, n, nameNode: PNode, k: TSymKind): JsonItem = for kind in genericParam.sym.typ.sons: param["types"].add %($kind) result.json["signature"]["genericParams"].add param + if optGenIndex in d.conf.globalOptions: + genItem(d, n, nameNode, k, kForceExport) + +proc setDoctype(d: PDoc, n: PNode) = + ## Processes `{.doctype.}` pragma changing Markdown/RST parsing options. + if n == nil: + return + if n.len != 2: + localError(d.conf, n.info, errUser, + "doctype pragma takes exactly 1 argument" + ) + return + var dt = "" + case n[1].kind + of nkStrLit: + dt = toLowerAscii(n[1].strVal) + of nkIdent: + dt = toLowerAscii(n[1].ident.s) + else: + localError(d.conf, n.info, errUser, + "unknown argument type $1 provided to doctype" % [$n[1].kind] + ) + return + case dt + of "markdown": + d.sharedState.options.incl roSupportMarkdown + d.sharedState.options.incl roPreferMarkdown + of "rstmarkdown": + d.sharedState.options.incl roSupportMarkdown + d.sharedState.options.excl roPreferMarkdown + of "rst": + d.sharedState.options.excl roSupportMarkdown + d.sharedState.options.excl roPreferMarkdown + else: + localError(d.conf, n.info, errUser, + ( + "unknown doctype value \"$1\", should be from " & + "\"RST\", \"Markdown\", \"RstMarkdown\"" + ) % [dt] + ) proc checkForFalse(n: PNode): bool = result = n.kind == nkIdent and cmpIgnoreStyle(n.ident.s, "false") == 0 @@ -1131,7 +1180,7 @@ proc traceDeps(d: PDoc, it: PNode) = for x in it[2]: a[2] = x traceDeps(d, a) - elif it.kind == nkSym and belongsToPackage(d.conf, it.sym): + elif it.kind == nkSym and belongsToProjectPackage(d.conf, it.sym): let external = externalDep(d, it.sym) if d.section[k].finalMarkup != "": d.section[k].finalMarkup.add(", ") dispA(d.conf, d.section[k].finalMarkup, @@ -1141,7 +1190,7 @@ proc traceDeps(d: PDoc, it: PNode) = proc exportSym(d: PDoc; s: PSym) = const k = exportSection - if s.kind == skModule and belongsToPackage(d.conf, s): + if s.kind == skModule and belongsToProjectPackage(d.conf, s): let external = externalDep(d, s) if d.section[k].finalMarkup != "": d.section[k].finalMarkup.add(", ") dispA(d.conf, d.section[k].finalMarkup, @@ -1150,7 +1199,7 @@ proc exportSym(d: PDoc; s: PSym) = changeFileExt(external, "html")]) elif s.kind != skModule and s.owner != nil: let module = originatingModule(s) - if belongsToPackage(d.conf, module): + if belongsToProjectPackage(d.conf, module): let complexSymbol = complexName(s.kind, s.ast, s.name.s) symbolOrId = d.newUniquePlainSymbol(complexSymbol) @@ -1211,8 +1260,9 @@ proc documentRaises*(cache: IdentCache; n: PNode) = let p3 = documentWriteEffect(cache, n, sfWrittenTo, "writes") let p4 = documentNewEffect(cache, n) let p5 = documentWriteEffect(cache, n, sfEscapes, "escapes") + let p6 = documentEffect(cache, n, pragmas, wForbids, forbiddenEffects) - if p1 != nil or p2 != nil or p3 != nil or p4 != nil or p5 != nil: + if p1 != nil or p2 != nil or p3 != nil or p4 != nil or p5 != nil or p6 != nil: if pragmas.kind == nkEmpty: n[pragmasPos] = newNodeI(nkPragma, n.info) if p1 != nil: n[pragmasPos].add p1 @@ -1220,6 +1270,7 @@ proc documentRaises*(cache: IdentCache; n: PNode) = if p3 != nil: n[pragmasPos].add p3 if p4 != nil: n[pragmasPos].add p4 if p5 != nil: n[pragmasPos].add p5 + if p6 != nil: n[pragmasPos].add p6 proc generateDoc*(d: PDoc, n, orig: PNode, docFlags: DocFlags = kDefault) = ## Goes through nim nodes recursively and collects doc comments. @@ -1231,6 +1282,8 @@ proc generateDoc*(d: PDoc, n, orig: PNode, docFlags: DocFlags = kDefault) = of nkPragma: let pragmaNode = findPragma(n, wDeprecated) d.modDeprecationMsg.add(genDeprecationMsg(d, pragmaNode)) + let doctypeNode = findPragma(n, wDoctype) + setDoctype(d, doctypeNode) of nkCommentStmt: d.modDescPre.add(genComment(d, n)) of nkProcDef, nkFuncDef: when useEffectSystem: documentRaises(d.cache, n) @@ -1291,6 +1344,7 @@ proc finishGenerateDoc*(d: var PDoc) = if fragment.isRst: firstRst = fragment.rst break + d.hasToc = d.hasToc or d.sharedState.hasToc preparePass2(d.sharedState, firstRst) # add anchors to overload groups before RST resolution @@ -1348,7 +1402,6 @@ proc finishGenerateDoc*(d: var PDoc) = d.section[k].secItems.clear renderItemPre(d, d.modDescPre, d.modDescFinal) d.modDescPre.setLen 0 - d.hasToc = d.hasToc or d.sharedState.hasToc # Finalize fragments of ``.json`` file for i, entry in d.jEntriesPre: @@ -1357,14 +1410,18 @@ proc finishGenerateDoc*(d: var PDoc) = var str: string renderRstToOut(d[], resolved, str) entry.json[entry.rstField] = %str - d.jEntriesFinal.add entry.json d.jEntriesPre[i].rst = nil + d.jEntriesFinal.add entry.json # generates docs + proc add(d: PDoc; j: JsonItem) = if j.json != nil or j.rst != nil: d.jEntriesPre.add j proc generateJson*(d: PDoc, n: PNode, includeComments: bool = true) = case n.kind + of nkPragma: + let doctypeNode = findPragma(n, wDoctype) + setDoctype(d, doctypeNode) of nkCommentStmt: if includeComments: d.add JsonItem(rst: genComment(d, n), rstField: "comment", @@ -1473,10 +1530,21 @@ proc genSection(d: PDoc, kind: TSymKind, groupedToc = false) = for item in d.tocSimple[kind].sorted(cmp): d.toc2[kind].add item.content - d.toc[kind] = getConfigVar(d.conf, "doc.section.toc") % [ - "sectionid", $ord(kind), "sectionTitle", title, - "sectionTitleID", $(ord(kind) + 50), "content", d.toc2[kind]] + let sectionValues = @[ + "sectionID", $ord(kind), "sectionTitleID", $(ord(kind) + 50), + "sectionTitle", title + ] + # Check if the toc has any children + if d.toc2[kind] != "": + # Use the dropdown version instead and store the children in the dropdown + d.toc[kind] = getConfigVar(d.conf, "doc.section.toc") % (sectionValues & @[ + "content", d.toc2[kind] + ]) + else: + # Just have the link + d.toc[kind] = getConfigVar(d.conf, "doc.section.toc_item") % sectionValues + proc relLink(outDir: AbsoluteDir, destFile: AbsoluteFile, linkto: RelativeFile): string = $relativeTo(outDir / linkto, destFile.splitFile().dir, '/') @@ -1514,7 +1582,7 @@ proc genOutFile(d: PDoc, groupedToc = false): string = "\\\\\\vspace{0.5em}\\large $1", [esc(d.target, d.meta[metaSubtitle])]) var groupsection = getConfigVar(d.conf, "doc.body_toc_groupsection") - let bodyname = if d.hasToc and not d.isPureRst and not d.conf.isLatexCmd: + let bodyname = if d.hasToc and not d.standaloneDoc and not d.conf.isLatexCmd: groupsection.setLen 0 "doc.body_toc_group" elif d.hasToc: "doc.body_toc" @@ -1598,6 +1666,8 @@ proc writeOutputJson*(d: PDoc, useWarning = false) = if optStdout in d.conf.globalOptions: write(stdout, $content) else: + let dir = d.destFile.splitFile.dir + createDir(dir) var f: File if open(f, d.destFile, fmWrite): write(f, $content) @@ -1620,17 +1690,18 @@ proc commandDoc*(cache: IdentCache, conf: ConfigRef) = handleDocOutputOptions conf var ast = parseFile(conf.projectMainIdx, cache, conf) if ast == nil: return - var d = newDocumentor(conf.projectFull, cache, conf) - d.hasToc = true + var d = newDocumentor(conf.projectFull, cache, conf, hasToc = true) generateDoc(d, ast, ast) finishGenerateDoc(d) writeOutput(d) generateIndex(d) proc commandRstAux(cache: IdentCache, conf: ConfigRef; - filename: AbsoluteFile, outExt: string) = + filename: AbsoluteFile, outExt: string, + preferMarkdown: bool) = var filen = addFileExt(filename, "txt") - var d = newDocumentor(filen, cache, conf, outExt, isPureRst = true) + var d = newDocumentor(filen, cache, conf, outExt, standaloneDoc = true, + preferMarkdown = preferMarkdown, hasToc = false) let rst = parseRst(readFile(filen.string), line=LineRstInit, column=ColRstInit, conf, d.sharedState) @@ -1639,22 +1710,23 @@ proc commandRstAux(cache: IdentCache, conf: ConfigRef; writeOutput(d) generateIndex(d) -proc commandRst2Html*(cache: IdentCache, conf: ConfigRef) = - commandRstAux(cache, conf, conf.projectFull, HtmlExt) +proc commandRst2Html*(cache: IdentCache, conf: ConfigRef, + preferMarkdown=false) = + commandRstAux(cache, conf, conf.projectFull, HtmlExt, preferMarkdown) -proc commandRst2TeX*(cache: IdentCache, conf: ConfigRef) = - commandRstAux(cache, conf, conf.projectFull, TexExt) +proc commandRst2TeX*(cache: IdentCache, conf: ConfigRef, + preferMarkdown=false) = + commandRstAux(cache, conf, conf.projectFull, TexExt, preferMarkdown) proc commandJson*(cache: IdentCache, conf: ConfigRef) = ## implementation of a deprecated jsondoc0 command var ast = parseFile(conf.projectMainIdx, cache, conf) if ast == nil: return - var d = newDocumentor(conf.projectFull, cache, conf) + var d = newDocumentor(conf.projectFull, cache, conf, hasToc = true) d.onTestSnippet = proc (d: var RstGenerator; filename, cmd: string; status: int; content: string) = localError(conf, newLineInfo(conf, AbsoluteFile d.filename, -1, -1), warnUser, "the ':test:' attribute is not supported by this backend") - d.hasToc = true generateJson(d, ast) finishGenerateDoc(d) let json = d.jEntriesFinal @@ -1673,12 +1745,11 @@ proc commandJson*(cache: IdentCache, conf: ConfigRef) = proc commandTags*(cache: IdentCache, conf: ConfigRef) = var ast = parseFile(conf.projectMainIdx, cache, conf) if ast == nil: return - var d = newDocumentor(conf.projectFull, cache, conf) + var d = newDocumentor(conf.projectFull, cache, conf, hasToc = true) d.onTestSnippet = proc (d: var RstGenerator; filename, cmd: string; status: int; content: string) = localError(conf, newLineInfo(conf, AbsoluteFile d.filename, -1, -1), warnUser, "the ':test:' attribute is not supported by this backend") - d.hasToc = true var content = "" generateTags(d, ast, content) @@ -1713,3 +1784,17 @@ proc commandBuildIndex*(conf: ConfigRef, dir: string, outFile = RelativeFile"") writeFile(filename, code) except: rawMessage(conf, errCannotOpenFile, filename.string) + +proc commandBuildIndexJson*(conf: ConfigRef, dir: string, outFile = RelativeFile"") = + var (modules, symbols, docs) = readIndexDir(dir) + let documents = toSeq(keys(Table[IndexEntry, seq[IndexEntry]](docs))) + let body = %*({"documents": documents, "modules": modules, "symbols": symbols}) + + var outFile = outFile + if outFile.isEmpty: outFile = theindexFname.RelativeFile.changeFileExt("") + let filename = getOutFile(conf, outFile, JsonExt) + + try: + writeFile(filename, $body) + except: + rawMessage(conf, errCannotOpenFile, filename.string) diff --git a/compiler/docgen2.nim b/compiler/docgen2.nim index bfdb4568ca2a..ea3253a2ca66 100644 --- a/compiler/docgen2.nim +++ b/compiler/docgen2.nim @@ -11,7 +11,7 @@ # semantic checking. import - options, ast, msgs, passes, docgen, lineinfos, pathutils + options, ast, msgs, passes, docgen, lineinfos, pathutils, packages from modulegraphs import ModuleGraph, PPassContext @@ -23,7 +23,7 @@ type PGen = ref TGen proc shouldProcess(g: PGen): bool = - (optWholeProject in g.doc.conf.globalOptions and g.module.getnimblePkgId == g.doc.conf.mainPackageId) or + (optWholeProject in g.doc.conf.globalOptions and g.doc.conf.belongsToProjectPackage(g.module)) or sfMainModule in g.module.flags or g.config.projectMainIdx == g.module.info.fileIndex template closeImpl(body: untyped) {.dirty.} = @@ -64,8 +64,7 @@ template myOpenImpl(ext: untyped) {.dirty.} = g.module = module g.config = graph.config var d = newDocumentor(AbsoluteFile toFullPath(graph.config, FileIndex module.position), - graph.cache, graph.config, ext, module) - d.hasToc = true + graph.cache, graph.config, ext, module, hasToc = true) g.doc = d result = g diff --git a/compiler/evalffi.nim b/compiler/evalffi.nim index 5004011ed319..37edef86c931 100644 --- a/compiler/evalffi.nim +++ b/compiler/evalffi.nim @@ -65,16 +65,16 @@ proc importcSymbol*(conf: ConfigRef, sym: PSym): PNode = if (lib.isNil or lib.kind == libHeader) and not gExeHandle.isNil: libPathMsg = "current exe: " & getAppFilename() & " nor libc: " & libcDll # first try this exe itself: - theAddr = gExeHandle.symAddr(name) + theAddr = gExeHandle.symAddr(name.cstring) # then try libc: if theAddr.isNil: let dllhandle = getDll(conf, gDllCache, libcDll, sym.info) - theAddr = dllhandle.symAddr(name) + theAddr = dllhandle.symAddr(name.cstring) elif not lib.isNil: let dll = if lib.kind == libHeader: libcDll else: lib.path.strVal libPathMsg = dll let dllhandle = getDll(conf, gDllCache, dll, sym.info) - theAddr = dllhandle.symAddr(name) + theAddr = dllhandle.symAddr(name.cstring) if theAddr.isNil: globalError(conf, sym.info, "cannot import symbol: " & name & " from " & libPathMsg) result.intVal = cast[ByteAddress](theAddr) @@ -110,8 +110,8 @@ proc mapCallConv(conf: ConfigRef, cc: TCallingConvention, info: TLineInfo): TABI else: globalError(conf, info, "cannot map calling convention to FFI") -template rd(T, p: untyped): untyped = (cast[ptr T](p))[] -template wr(T, p, v: untyped): untyped = (cast[ptr T](p))[] = v +template rd(typ, p: untyped): untyped = (cast[ptr typ](p))[] +template wr(typ, p, v: untyped): untyped = (cast[ptr typ](p))[] = v template `+!`(x, y: untyped): untyped = cast[pointer](cast[ByteAddress](x) + y) @@ -177,8 +177,8 @@ const maxPackDepth = 20 var packRecCheck = 0 proc pack(conf: ConfigRef, v: PNode, typ: PType, res: pointer) = - template awr(T, v: untyped): untyped = - wr(T, res, v) + template awr(typ, v: untyped): untyped = + wr(typ, res, v) case typ.kind of tyBool: awr(bool, v.intVal != 0) diff --git a/compiler/extccomp.nim b/compiler/extccomp.nim index 6535b30d64c6..e292f200c29b 100644 --- a/compiler/extccomp.nim +++ b/compiler/extccomp.nim @@ -12,9 +12,11 @@ # from a lineinfos file, to provide generalized procedures to compile # nim files. -import ropes, platform, condsyms, options, msgs, lineinfos, pathutils +import ropes, platform, condsyms, options, msgs, lineinfos, pathutils, modulepaths -import std/[os, strutils, osproc, sha1, streams, sequtils, times, strtabs, json, jsonutils, sugar] +import std/[os, osproc, sha1, streams, sequtils, times, strtabs, json, jsonutils, sugar, parseutils] + +import std / strutils except addf when defined(nimPreviewSlimSystem): import std/syncio @@ -86,10 +88,10 @@ compiler gcc: linkLibCmd: " -l$1", debug: "", pic: "-fPIC", - asmStmtFrmt: "asm($1);$n", + asmStmtFrmt: "__asm__($1);$n", structStmtFmt: "$1 $3 $2 ", # struct|union [packed] $name produceAsm: gnuAsmListing, - cppXsupport: "-std=gnu++14 -funsigned-char", + cppXsupport: "-std=gnu++17 -funsigned-char", props: {hasSwitchRange, hasComputedGoto, hasCpp, hasGcGuard, hasGnuAsm, hasAttribute}) @@ -116,7 +118,7 @@ compiler nintendoSwitchGCC: asmStmtFrmt: "asm($1);$n", structStmtFmt: "$1 $3 $2 ", # struct|union [packed] $name produceAsm: gnuAsmListing, - cppXsupport: "-std=gnu++14 -funsigned-char", + cppXsupport: "-std=gnu++17 -funsigned-char", props: {hasSwitchRange, hasComputedGoto, hasCpp, hasGcGuard, hasGnuAsm, hasAttribute}) @@ -153,7 +155,7 @@ compiler vcc: compileTmpl: "/c$vccplatform $options $include /nologo /Fo$objfile $file", buildGui: " /SUBSYSTEM:WINDOWS user32.lib ", buildDll: " /LD", - buildLib: "lib /OUT:$libfile $objfiles", + buildLib: "vccexe --command:lib$vccplatform /nologo /OUT:$libfile $objfiles", linkerExe: "cl", linkTmpl: "$builddll$vccplatform /Fe$exefile $objfiles $buildgui /nologo $options", includeCmd: " /I", @@ -397,6 +399,7 @@ proc initVars*(conf: ConfigRef) = proc completeCfilePath*(conf: ConfigRef; cfile: AbsoluteFile, createSubDir: bool = true): AbsoluteFile = + ## Generate the absolute file path to the generated modules. result = completeGeneratedFilePath(conf, cfile, createSubDir) proc toObjFile*(conf: ConfigRef; filename: AbsoluteFile): AbsoluteFile = @@ -407,7 +410,7 @@ proc addFileToCompile*(conf: ConfigRef; cf: Cfile) = conf.toCompile.add(cf) proc addLocalCompileOption*(conf: ConfigRef; option: string; nimfile: AbsoluteFile) = - let key = completeCfilePath(conf, withPackageName(conf, nimfile)).string + let key = completeCfilePath(conf, mangleModuleName(conf, nimfile).AbsoluteFile).string var value = conf.cfileSpecificOptions.getOrDefault(key) if strutils.find(value, option, 0) < 0: addOpt(value, option) @@ -460,7 +463,13 @@ proc noAbsolutePaths(conf: ConfigRef): bool {.inline.} = # really: Cross compilation from Linux to Linux for example is entirely # reasonable. # `optGenMapping` is included here for niminst. - result = conf.globalOptions * {optGenScript, optGenMapping} != {} + # We use absolute paths for vcc / cl, see issue #19883. + let options = + if conf.cCompiler == ccVcc: + {optGenMapping} + else: + {optGenScript, optGenMapping} + result = conf.globalOptions * options != {} proc cFileSpecificOptions(conf: ConfigRef; nimname, fullNimFile: string): string = result = conf.compileOptions @@ -513,7 +522,10 @@ proc needsExeExt(conf: ConfigRef): bool {.inline.} = (conf.target.hostOS == osWindows) proc useCpp(conf: ConfigRef; cfile: AbsoluteFile): bool = - conf.backend == backendCpp and not cfile.string.endsWith(".c") + # List of possible file extensions taken from gcc + for ext in [".C", ".cc", ".cpp", ".CPP", ".c++", ".cp", ".cxx"]: + if cfile.string.endsWith(ext): return true + false proc envFlags(conf: ConfigRef): string = result = if conf.backend == backendCpp: @@ -521,14 +533,14 @@ proc envFlags(conf: ConfigRef): string = else: getEnv("CFLAGS") -proc getCompilerExe(conf: ConfigRef; compiler: TSystemCC; cfile: AbsoluteFile): string = +proc getCompilerExe(conf: ConfigRef; compiler: TSystemCC; isCpp: bool): string = if compiler == ccEnv: - result = if useCpp(conf, cfile): + result = if isCpp: getEnv("CXX") else: getEnv("CC") else: - result = if useCpp(conf, cfile): + result = if isCpp: CC[compiler].cppCompiler else: CC[compiler].compilerExe @@ -542,47 +554,36 @@ proc ccHasSaneOverflow*(conf: ConfigRef): bool = result = false # assume an old or crappy GCC var exe = getConfigVar(conf, conf.cCompiler, ".exe") if exe.len == 0: exe = CC[conf.cCompiler].compilerExe - let (s, exitCode) = try: execCmdEx(exe & " --version") except: ("", 1) + # NOTE: should we need the full version, use -dumpfullversion + let (s, exitCode) = try: execCmdEx(exe & " -dumpversion") except: ("", 1) if exitCode == 0: - var i = 0 - var j = 0 - # the version is the last part of the first line: - while i < s.len and s[i] != '\n': - if s[i] in {' ', '\t'}: j = i+1 - inc i - if j > 0: - var major = 0 - while j < s.len and s[j] in {'0'..'9'}: - major = major * 10 + (ord(s[j]) - ord('0')) - inc j - if i < s.len and s[j] == '.': inc j - while j < s.len and s[j] in {'0'..'9'}: - inc j - if j+1 < s.len and s[j] == '.' and s[j+1] in {'0'..'9'}: - # we found a third version number, chances are high - # we really parsed the version: - result = major >= 5 + var major: int + discard parseInt(s, major) + result = major >= 5 else: result = conf.cCompiler == ccCLang proc getLinkerExe(conf: ConfigRef; compiler: TSystemCC): string = result = if CC[compiler].linkerExe.len > 0: CC[compiler].linkerExe - elif optMixedMode in conf.globalOptions and conf.backend != backendCpp: CC[compiler].cppCompiler - else: getCompilerExe(conf, compiler, AbsoluteFile"") + else: getCompilerExe(conf, compiler, optMixedMode in conf.globalOptions or conf.backend == backendCpp) proc getCompileCFileCmd*(conf: ConfigRef; cfile: Cfile, isMainFile = false; produceOutput = false): string = - let c = conf.cCompiler + let + c = conf.cCompiler + isCpp = useCpp(conf, cfile.cname) # We produce files like module.nim.cpp, so the absolute Nim filename is not # cfile.name but `cfile.cname.changeFileExt("")`: var options = cFileSpecificOptions(conf, cfile.nimname, cfile.cname.changeFileExt("").string) - if useCpp(conf, cfile.cname): + if isCpp: # needs to be prepended so that --passc:-std=c++17 can override default. # we could avoid allocation by making cFileSpecificOptions inplace options = CC[c].cppXsupport & ' ' & options + # If any C++ file was compiled, we need to use C++ driver for linking as well + incl conf.globalOptions, optMixedMode var exe = getConfigVar(conf, c, ".exe") - if exe.len == 0: exe = getCompilerExe(conf, c, cfile.cname) + if exe.len == 0: exe = getCompilerExe(conf, c, isCpp) if needsExeExt(conf): exe = addFileExt(exe, "exe") if (optGenDynLib in conf.globalOptions or (conf.hcrOn and not isMainFile)) and @@ -602,7 +603,7 @@ proc getCompileCFileCmd*(conf: ConfigRef; cfile: Cfile, compilePattern = joinPath(conf.cCompilerPath, exe) else: - compilePattern = getCompilerExe(conf, c, cfile.cname) + compilePattern = getCompilerExe(conf, c, isCpp) includeCmd.add(join([CC[c].includeCmd, quoteShell(conf.projectPath.string)])) @@ -644,7 +645,7 @@ proc getCompileCFileCmd*(conf: ConfigRef; cfile: Cfile, "for the selected C compiler: " & CC[conf.cCompiler].name) result.add(' ') - result.addf(CC[c].compileTmpl, [ + strutils.addf(result, CC[c].compileTmpl, [ "dfile", dfile, "file", cfsh, "objfile", quoteShell(objfile), "options", options, "include", includeCmd, @@ -664,7 +665,7 @@ proc footprint(conf: ConfigRef; cfile: Cfile): SecureHash = proc externalFileChanged(conf: ConfigRef; cfile: Cfile): bool = if conf.backend == backendJs: return false # pre-existing behavior, but not sure it's good - let hashFile = toGeneratedFile(conf, conf.withPackageName(cfile.cname), "sha1") + let hashFile = toGeneratedFile(conf, conf.mangleModuleName(cfile.cname).AbsoluteFile, "sha1") let currentHash = footprint(conf, cfile) var f: File if open(f, hashFile.string, fmRead): @@ -702,7 +703,8 @@ proc getLinkCmd(conf: ConfigRef; output: AbsoluteFile, if removeStaticFile: removeFile output # fixes: bug #16947 result = CC[conf.cCompiler].buildLib % ["libfile", quoteShell(output), - "objfiles", objfiles] + "objfiles", objfiles, + "vccplatform", vccplatform(conf)] else: var linkerExe = getConfigVar(conf, conf.cCompiler, ".linkerexe") if linkerExe.len == 0: linkerExe = getLinkerExe(conf, conf.cCompiler) @@ -735,7 +737,7 @@ proc getLinkCmd(conf: ConfigRef; output: AbsoluteFile, "buildgui", buildgui, "options", linkOptions, "objfiles", objfiles, "exefile", exefile, "nim", getPrefixDir(conf).string, "lib", conf.libpath.string]) result.add ' ' - result.addf(linkTmpl, ["builddll", builddll, + strutils.addf(result, linkTmpl, ["builddll", builddll, "mapfile", mapfile, "buildgui", buildgui, "options", linkOptions, "objfiles", objfiles, "exefile", exefile, @@ -872,9 +874,9 @@ proc hcrLinkTargetName(conf: ConfigRef, objFile: string, isMain = false): Absolu proc displayProgressCC(conf: ConfigRef, path, compileCmd: string): string = if conf.hasHint(hintCC): if optListCmd in conf.globalOptions or conf.verbosity > 1: - result = MsgKindToStr[hintCC] % (demanglePackageName(path.splitFile.name) & ": " & compileCmd) + result = MsgKindToStr[hintCC] % (demangleModuleName(path.splitFile.name) & ": " & compileCmd) else: - result = MsgKindToStr[hintCC] % demanglePackageName(path.splitFile.name) + result = MsgKindToStr[hintCC] % demangleModuleName(path.splitFile.name) proc callCCompiler*(conf: ConfigRef) = var @@ -884,7 +886,7 @@ proc callCCompiler*(conf: ConfigRef) = return # speed up that call if only compiling and no script shall be # generated #var c = cCompiler - var script: Rope = nil + var script: Rope = "" var cmds: TStringSeq var prettyCmds: TStringSeq let prettyCb = proc (idx: int) = writePrettyCmdsStderr(prettyCmds[idx]) @@ -947,7 +949,7 @@ proc callCCompiler*(conf: ConfigRef) = objfiles.add(' ') objfiles.add(quoteShell(objFile)) let mainOutput = if optGenScript notin conf.globalOptions: conf.prepareToWriteOutput - else: AbsoluteFile(conf.projectName) + else: AbsoluteFile(conf.outFile) linkCmd = getLinkCmd(conf, mainOutput, objfiles, removeStaticFile = true) extraCmds = getExtraCmds(conf, mainOutput) diff --git a/compiler/guards.nim b/compiler/guards.nim index a50593aca691..f14c1d91553e 100644 --- a/compiler/guards.nim +++ b/compiler/guards.nim @@ -200,7 +200,7 @@ proc highBound*(conf: ConfigRef; x: PNode; o: Operators): PNode = nkIntLit.newIntNode(lastOrd(conf, typ)) elif typ.kind == tySequence and x.kind == nkSym and x.sym.kind == skConst: - nkIntLit.newIntNode(x.sym.ast.len-1) + nkIntLit.newIntNode(x.sym.astdef.len-1) else: o.opAdd.buildCall(o.opLen.buildCall(x), minusOne()) result.info = x.info diff --git a/compiler/hlo.nim b/compiler/hlo.nim index 8620c4c4d134..e3670312e07c 100644 --- a/compiler/hlo.nim +++ b/compiler/hlo.nim @@ -8,6 +8,7 @@ # # This include implements the high level optimization pass. +# included from sem.nim when defined(nimPreviewSlimSystem): import std/assertions diff --git a/compiler/ic/bitabs.nim b/compiler/ic/bitabs.nim index ae673b574c39..8adab8388024 100644 --- a/compiler/ic/bitabs.nim +++ b/compiler/ic/bitabs.nim @@ -93,13 +93,13 @@ proc getOrIncl*[T](t: var BiTable[T]; v: T): LitId = t.vals.add v -proc `[]`*[T](t: var BiTable[T]; LitId: LitId): var T {.inline.} = - let idx = idToIdx LitId +proc `[]`*[T](t: var BiTable[T]; litId: LitId): var T {.inline.} = + let idx = idToIdx litId assert idx < t.vals.len result = t.vals[idx] -proc `[]`*[T](t: BiTable[T]; LitId: LitId): lent T {.inline.} = - let idx = idToIdx LitId +proc `[]`*[T](t: BiTable[T]; litId: LitId): lent T {.inline.} = + let idx = idToIdx litId assert idx < t.vals.len result = t.vals[idx] diff --git a/compiler/ic/cbackend.nim b/compiler/ic/cbackend.nim index e7ab000e69de..815078a360a2 100644 --- a/compiler/ic/cbackend.nim +++ b/compiler/ic/cbackend.nim @@ -24,7 +24,7 @@ when defined(nimPreviewSlimSystem): import std/assertions import ".."/[ast, options, lineinfos, modulegraphs, cgendata, cgen, - pathutils, extccomp, msgs] + pathutils, extccomp, msgs, modulepaths] import packed_ast, ic, dce, rodfiles @@ -64,7 +64,8 @@ proc addFileToLink(config: ConfigRef; m: PSym) = if config.backend == backendCpp: ".nim.cpp" elif config.backend == backendObjc: ".nim.m" else: ".nim.c" - let cfile = changeFileExt(completeCfilePath(config, withPackageName(config, filename)), ext) + let cfile = changeFileExt(completeCfilePath(config, + mangleModuleName(config, filename).AbsoluteFile), ext) let objFile = completeCfilePath(config, toObjFile(config, cfile)) if fileExists(objFile): var cf = Cfile(nimname: m.name.s, cname: cfile, diff --git a/compiler/ic/ic.nim b/compiler/ic/ic.nim index 193e5f517586..866237f08368 100644 --- a/compiler/ic/ic.nim +++ b/compiler/ic/ic.nim @@ -10,12 +10,12 @@ import hashes, tables, intsets, std/sha1 import packed_ast, bitabs, rodfiles import ".." / [ast, idents, lineinfos, msgs, ropes, options, - pathutils, condsyms] + pathutils, condsyms, packages, modulepaths] #import ".." / [renderer, astalgo] from os import removeFile, isAbsolute when defined(nimPreviewSlimSystem): - import std/[syncio, assertions] + import std/[syncio, assertions, formatfloat] type PackedConfig* = object @@ -403,7 +403,7 @@ proc storeSym*(s: PSym; c: var PackedEncoder; m: var PackedModule): PackedItemId p.bitsize = s.bitsize p.alignment = s.alignment - p.externalName = toLitId(if s.loc.r.isNil: "" else: $s.loc.r, m) + p.externalName = toLitId(s.loc.r, m) p.locFlags = s.loc.flags c.addMissing s.typ p.typ = s.typ.storeType(c, m) @@ -551,6 +551,10 @@ proc loadError(err: RodFileError; filename: AbsoluteFile; config: ConfigRef;) = rawMessage(config, warnCannotOpenFile, filename.string & " reason: " & $err) #echo "Error: ", $err, " loading file: ", filename.string +proc toRodFile*(conf: ConfigRef; f: AbsoluteFile; ext = RodExt): AbsoluteFile = + result = changeFileExt(completeGeneratedFilePath(conf, + mangleModuleName(conf, f).AbsoluteFile), ext) + proc loadRodFile*(filename: AbsoluteFile; m: var PackedModule; config: ConfigRef; ignoreConfig = false): RodFileError = var f = rodfiles.open(filename.string) @@ -930,17 +934,6 @@ proc loadType(c: var PackedDecoder; g: var PackedModuleGraph; thisModule: int; t result = g[si].types[t.item] assert result.itemId.item > 0 -proc newPackage(config: ConfigRef; cache: IdentCache; fileIdx: FileIndex): PSym = - let filename = AbsoluteFile toFullPath(config, fileIdx) - let name = getIdent(cache, splitFile(filename).name) - let info = newLineInfo(fileIdx, 1, 1) - let - pck = getPackageName(config, filename.string) - pck2 = if pck.len > 0: pck else: "unknown" - pack = getIdent(cache, pck2) - result = newSym(skPackage, getIdent(cache, pck2), - ItemId(module: PackageModuleId, item: int32(fileIdx)), nil, info) - proc setupLookupTables(g: var PackedModuleGraph; conf: ConfigRef; cache: IdentCache; fileIdx: FileIndex; m: var LoadedModule) = m.iface = initTable[PIdent, seq[PackedItemId]]() @@ -968,7 +961,7 @@ proc setupLookupTables(g: var PackedModuleGraph; conf: ConfigRef; cache: IdentCa name: getIdent(cache, splitFile(filename).name), info: newLineInfo(fileIdx, 1, 1), position: int(fileIdx)) - m.module.owner = newPackage(conf, cache, fileIdx) + m.module.owner = getPackage(conf, cache, fileIdx) m.module.flags = m.fromDisk.moduleFlags proc loadToReplayNodes(g: var PackedModuleGraph; conf: ConfigRef; cache: IdentCache; diff --git a/compiler/ic/rodfiles.nim b/compiler/ic/rodfiles.nim index d811b9b99b45..496db05647ca 100644 --- a/compiler/ic/rodfiles.nim +++ b/compiler/ic/rodfiles.nim @@ -37,8 +37,7 @@ when defined(nimPreviewSlimSystem): ## ## Now read the bits below to understand what's missing. ## -## Issues with the Example -## ``````````````````````` +## ### Issues with the Example ## Missing Sections: ## This is a low level API, so headers and sections need to be stored and ## loaded by the user, see `storeHeader` & `loadHeader` and `storeSection` & diff --git a/compiler/injectdestructors.nim b/compiler/injectdestructors.nim index 6500c5bc774d..63cfbbd9f494 100644 --- a/compiler/injectdestructors.nim +++ b/compiler/injectdestructors.nim @@ -80,12 +80,6 @@ import sets, hashes proc hash(n: PNode): Hash = hash(cast[pointer](n)) -proc aliasesCached(cache: var Table[(PNode, PNode), AliasKind], obj, field: PNode): AliasKind = - let key = (obj, field) - if not cache.hasKey(key): - cache[key] = aliases(obj, field) - cache[key] - type State = ref object lastReads: IntSet @@ -116,9 +110,8 @@ proc mergeStates(a: var State, b: sink State) = a.alreadySeen.incl b.alreadySeen proc computeLastReadsAndFirstWrites(cfg: ControlFlowGraph) = - var cache = initTable[(PNode, PNode), AliasKind]() template aliasesCached(obj, field: PNode): AliasKind = - aliasesCached(cache, obj, field) + aliases(obj, field) var cfg = cfg preprocessCfg(cfg) @@ -576,7 +569,7 @@ proc processScope(c: var Con; s: var Scope; ret: PNode): PNode = template processScopeExpr(c: var Con; s: var Scope; ret: PNode, processCall: untyped): PNode = assert not ret.typ.isEmptyType - var result = newNodeI(nkStmtListExpr, ret.info) + var result = newNodeIT(nkStmtListExpr, ret.info, ret.typ) # There is a possibility to do this check: s.wasMoved.len > 0 or s.final.len > 0 # later and use it to eliminate the temporary when theres no need for it, but its # tricky because you would have to intercept moveOrCopy at a certain point @@ -873,9 +866,9 @@ proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode): PNode = if it.kind == nkVarTuple and hasDestructor(c, ri.typ): let x = lowerTupleUnpacking(c.graph, it, c.idgen, c.owner) result.add p(x, c, s, consumed) - elif it.kind == nkIdentDefs and hasDestructor(c, it[0].typ): + elif it.kind == nkIdentDefs and hasDestructor(c, skipPragmaExpr(it[0]).typ): for j in 0.. can stay expression based - if n.kind in nkCallKinds+{nkBracketExpr, nkDotExpr, nkPar, nkTupleConstr} or - (n.kind in {nkObjConstr, nkBracket, nkCurly}): + case n.kind + of nkCallKinds, nkBracketExpr, nkDotExpr, nkPar, nkTupleConstr, + nkObjConstr, nkBracket, nkCurly, + nkDerefExpr, nkHiddenDeref, nkAddr, nkHiddenAddr, + nkConv, nkHiddenStdConv, nkHiddenSubConv: for c in n: if not p.isSimpleExpr(c): return false result = true - elif n.isAtom: - result = true + of nkStmtListExpr: + for i in 0.. 1: lineF(p, "}$n", []) else: - var orExpr: Rope = nil + var orExpr: Rope = "" var excAlias: PNode = nil useMagic(p, "isObj") @@ -803,13 +828,13 @@ proc genTry(p: PProc, n: PNode, r: var TCompRes) = excAlias = it[2] # If this is a ``except exc as sym`` branch there must be no following # nodes - doAssert orExpr == nil + doAssert orExpr == "" elif it.kind == nkType: throwObj = it else: internalError(p.config, n.info, "genTryStmt") - if orExpr != nil: orExpr.add("||") + if orExpr != "": orExpr.add("||") # Generate the correct type checking code depending on whether this is a # NIM-native or a JS-native exception # if isJsObject(throwObj.typ): @@ -861,14 +886,19 @@ proc genRaiseStmt(p: PProc, n: PNode) = proc genCaseJS(p: PProc, n: PNode, r: var TCompRes) = var - cond, stmt: TCompRes + a, b, cond, stmt: TCompRes totalRange = 0 genLineDir(p, n) gen(p, n[0], cond) - let stringSwitch = skipTypes(n[0].typ, abstractVar).kind == tyString - if stringSwitch: + let typeKind = skipTypes(n[0].typ, abstractVar).kind + var transferRange = false + let anyString = typeKind in {tyString, tyCstring} + case typeKind + of tyString: useMagic(p, "toJSStr") lineF(p, "switch (toJSStr($1)) {$n", [cond.rdLoc]) + of tyFloat..tyFloat128: + transferRange = true else: lineF(p, "switch ($1) {$n", [cond.rdLoc]) if not isEmptyType(n.typ): @@ -876,41 +906,75 @@ proc genCaseJS(p: PProc, n: PNode, r: var TCompRes) = r.res = getTemp(p) for i in 1.. 65535: - localError(p.config, n.info, - "Your case statement contains too many branches, consider using if/else instead!") - while v.intVal <= e[1].intVal: - gen(p, v, cond) - lineF(p, "case $1:$n", [cond.rdLoc]) - inc(v.intVal) + if transferRange: + gen(p, e[0], a) + gen(p, e[1], b) + if j != itLen - 2: + lineF(p, "$1 >= $2 && $1 <= $3 || $n", [cond.rdLoc, a.rdLoc, b.rdLoc]) + else: + lineF(p, "$1 >= $2 && $1 <= $3", [cond.rdLoc, a.rdLoc, b.rdLoc]) + else: + var v = copyNode(e[0]) + inc(totalRange, int(e[1].intVal - v.intVal)) + if totalRange > 65535: + localError(p.config, n.info, + "Your case statement contains too many branches, consider using if/else instead!") + while v.intVal <= e[1].intVal: + gen(p, v, cond) + lineF(p, "case $1:$n", [cond.rdLoc]) + inc(v.intVal) else: - if stringSwitch: + if anyString: case e.kind of nkStrLit..nkTripleStrLit: lineF(p, "case $1:$n", [makeJSString(e.strVal, false)]) + of nkNilLit: lineF(p, "case null:$n", []) else: internalError(p.config, e.info, "jsgen.genCaseStmt: 2") else: - gen(p, e, cond) - lineF(p, "case $1:$n", [cond.rdLoc]) + if transferRange: + gen(p, e, a) + if j != itLen - 2: + lineF(p, "$1 == $2 || $n", [cond.rdLoc, a.rdLoc]) + else: + lineF(p, "$1 == $2", [cond.rdLoc, a.rdLoc]) + else: + gen(p, e, a) + lineF(p, "case $1:$n", [a.rdLoc]) + if transferRange: + lineF(p, "){", []) p.nested: gen(p, lastSon(it), stmt) moveInto(p, stmt, r) - lineF(p, "break;$n", []) + if transferRange: + lineF(p, "}$n", []) + else: + lineF(p, "break;$n", []) of nkElse: - lineF(p, "default: $n", []) + if transferRange: + lineF(p, "else{$n", []) + else: + lineF(p, "default: $n", []) p.nested: gen(p, it[0], stmt) moveInto(p, stmt, r) - lineF(p, "break;$n", []) + if transferRange: + lineF(p, "}$n", []) + else: + lineF(p, "break;$n", []) else: internalError(p.config, it.info, "jsgen.genCaseStmt") - lineF(p, "}$n", []) + if not transferRange: + lineF(p, "}$n", []) proc genBlock(p: PProc, n: PNode, r: var TCompRes) = inc(p.unique) @@ -922,12 +986,12 @@ proc genBlock(p: PProc, n: PNode, r: var TCompRes) = sym.loc.k = locOther sym.position = idx+1 let labl = p.unique - lineF(p, "Label$1: do {$n", [labl.rope]) + lineF(p, "Label$1: {$n", [labl.rope]) setLen(p.blocks, idx + 1) p.blocks[idx].id = - p.unique # negative because it isn't used yet gen(p, n[1], r) setLen(p.blocks, idx) - lineF(p, "} while (false);$n", [labl.rope]) + lineF(p, "};$n", [labl.rope]) proc genBreakStmt(p: PProc, n: PNode) = var idx: int @@ -949,7 +1013,7 @@ proc genBreakStmt(p: PProc, n: PNode) = proc genAsmOrEmitStmt(p: PProc, n: PNode) = genLineDir(p, n) - p.body.add p.indentLine(nil) + p.body.add p.indentLine("") for i in 0.. 0 if pat.contains({'#', '(', '@'}): var typ = skipTypes(n[0].typ, abstractInst) @@ -1622,10 +1686,10 @@ proc genInfixCall(p: PProc, n: PNode, r: var TCompRes) = if n.len != 1: gen(p, n[1], r) if r.typ == etyBaseIndex: - if r.address == nil: + if r.address == "": globalError(p.config, n.info, "cannot invoke with infix syntax") r.res = "$1[$2]" % [r.address, r.res] - r.address = nil + r.address = "" r.typ = etyNone r.res.add(".") var op: TCompRes @@ -1693,16 +1757,22 @@ proc createObjInitList(p: PProc, typ: PType, excludedFieldIDs: IntSet, output: v t = t[0] proc arrayTypeForElemType(typ: PType): string = - # XXX This should also support tyEnum and tyBool + let typ = typ.skipTypes(abstractRange) case typ.kind of tyInt, tyInt32: "Int32Array" of tyInt16: "Int16Array" of tyInt8: "Int8Array" of tyUInt, tyUInt32: "Uint32Array" of tyUInt16: "Uint16Array" - of tyUInt8: "Uint8Array" + of tyUInt8, tyChar, tyBool: "Uint8Array" of tyFloat32: "Float32Array" of tyFloat64, tyFloat: "Float64Array" + of tyEnum: + case typ.size + of 1: "Uint8Array" + of 2: "Uint16Array" + of 4: "Uint32Array" + else: "" else: "" proc createVar(p: PProc, typ: PType, indirect: bool): Rope = @@ -1771,12 +1841,12 @@ proc createVar(p: PProc, typ: PType, indirect: bool): Rope = result = createVar(p, lastSon t, indirect) else: internalError(p.config, "createVar: " & $t.kind) - result = nil + result = "" else: internalError(p.config, "createVar: " & $t.kind) - result = nil + result = "" -template returnType: untyped = ~"" +template returnType: untyped = "" proc genVarInit(p: PProc, v: PSym, n: PNode) = var @@ -1874,10 +1944,9 @@ proc genVarStmt(p: PProc, n: PNode) = proc genConstant(p: PProc, c: PSym) = if lfNoDecl notin c.loc.flags and not p.g.generatedSyms.containsOrIncl(c.id): - let oldBody = p.body - p.body = nil - #genLineDir(p, c.ast) - genVarInit(p, c, c.ast) + let oldBody = move p.body + #genLineDir(p, c.astdef) + genVarInit(p, c, c.astdef) p.g.constants.add(p.body) p.body = oldBody @@ -1929,7 +1998,7 @@ proc genConStrStr(p: PProc, n: PNode, r: var TCompRes) = else: r.res.add("$1 || [])" % [a.res]) -proc genReprAux(p: PProc, n: PNode, r: var TCompRes, magic: string, typ: Rope = nil) = +proc genReprAux(p: PProc, n: PNode, r: var TCompRes, magic: string, typ: Rope = "") = useMagic(p, magic) r.res.add(magic & "(") var a: TCompRes @@ -1938,7 +2007,7 @@ proc genReprAux(p: PProc, n: PNode, r: var TCompRes, magic: string, typ: Rope = if magic == "reprAny": # the pointer argument in reprAny is expandend to # (pointedto, pointer), so we need to fill it - if a.address.isNil: + if a.address.len == 0: r.res.add(a.res) r.res.add(", null") else: @@ -1946,14 +2015,14 @@ proc genReprAux(p: PProc, n: PNode, r: var TCompRes, magic: string, typ: Rope = else: r.res.add(a.res) - if not typ.isNil: + if typ != "": r.res.add(", ") r.res.add(typ) r.res.add(")") proc genRepr(p: PProc, n: PNode, r: var TCompRes) = let t = skipTypes(n[1].typ, abstractVarRange) - case t.kind: + case t.kind of tyInt..tyInt64, tyUInt..tyUInt64: genReprAux(p, n, r, "reprInt") of tyChar: @@ -2102,6 +2171,8 @@ proc genMagic(p: PProc, n: PNode, r: var TCompRes) = gen(p, n[1], x) useMagic(p, "nimCopy") r.res = "nimCopy(null, $1, $2)" % [x.rdLoc, genTypeInfo(p, n.typ)] + of mOpenArrayToSeq: + genCall(p, n, r) of mDestroy, mTrace: discard "ignore calls to the default destructor" of mOrd: genOrd(p, n, r) of mLengthStr, mLengthSeq, mLengthOpenArray, mLengthArray: @@ -2262,6 +2333,7 @@ proc genObjConstr(p: PProc, n: PNode, r: var TCompRes) = r.kind = resExpr var initList : Rope var fieldIDs = initIntSet() + let nTyp = n.typ.skipTypes(abstractInst) for i in 1.. 1: initList.add(", ") var it = n[i] @@ -2269,8 +2341,8 @@ proc genObjConstr(p: PProc, n: PNode, r: var TCompRes) = let val = it[1] gen(p, val, a) var f = it[0].sym - if f.loc.r == nil: f.loc.r = mangleName(p.module, f) - fieldIDs.incl(lookupFieldAgain(n.typ, f).id) + if f.loc.r == "": f.loc.r = mangleName(p.module, f) + fieldIDs.incl(lookupFieldAgain(nTyp, f).id) let typ = val.typ.skipTypes(abstractInst) if a.typ == etyBaseIndex: @@ -2330,7 +2402,7 @@ proc convStrToCStr(p: PProc, n: PNode, r: var TCompRes) = gen(p, n[0][0], r) else: gen(p, n[0], r) - if r.res == nil: internalError(p.config, n.info, "convStrToCStr") + if r.res == "": internalError(p.config, n.info, "convStrToCStr") useMagic(p, "toJSStr") r.res = "toJSStr($1)" % [r.res] r.kind = resExpr @@ -2342,7 +2414,7 @@ proc convCStrToStr(p: PProc, n: PNode, r: var TCompRes) = gen(p, n[0][0], r) else: gen(p, n[0], r) - if r.res == nil: internalError(p.config, n.info, "convCStrToStr") + if r.res == "": internalError(p.config, n.info, "convCStrToStr") useMagic(p, "cstrToNimstr") r.res = "cstrToNimstr($1)" % [r.res] r.kind = resExpr @@ -2372,11 +2444,11 @@ proc genProcBody(p: PProc, prc: PSym): Rope = makeJSString(prc.owner.name.s & '.' & prc.name.s), makeJSString(toFilenameOption(p.config, prc.info.fileIndex, foStacktrace))) else: - result = nil + result = "" if p.beforeRetNeeded: - result.add p.indentLine(~"BeforeRet: do {$n") + result.add p.indentLine("BeforeRet: {\n") result.add p.body - result.add p.indentLine(~"} while (false);$n") + result.add p.indentLine("};\n") else: result.add(p.body) if prc.typ.callConv == ccSysCall: @@ -2386,12 +2458,13 @@ proc genProcBody(p: PProc, prc: PSym): Rope = result.add(frameDestroy(p)) proc optionalLine(p: Rope): Rope = - if p == nil: - return nil + if p == "": + return "" else: return p & "\L" proc genProc(oldProc: PProc, prc: PSym): Rope = + ## Generate a JS procedure ('function'). var resultSym: PSym a: TCompRes @@ -2399,8 +2472,8 @@ proc genProc(oldProc: PProc, prc: PSym): Rope = # echo "BEGIN generating code for: " & prc.name.s var p = newProc(oldProc.g, oldProc.module, prc.ast, prc.options) p.up = oldProc - var returnStmt: Rope = nil - var resultAsgn: Rope = nil + var returnStmt: Rope = "" + var resultAsgn: Rope = "" var name = mangleName(p.module, prc) let header = generateHeader(p, prc.typ) if prc.typ[0] != nil and sfPure notin prc.flags: @@ -2444,7 +2517,7 @@ proc genProc(oldProc: PProc, prc: PSym): Rope = optionalLine(p.indentLine(returnStmt))]) else: # if optLineDir in p.config.options: - # result.add(~"\L") + # result.add("\L") if p.config.hcrOn: # Here, we introduce thunks that create the equivalent of a jump table @@ -2467,7 +2540,7 @@ proc genProc(oldProc: PProc, prc: PSym): Rope = dec p.extraIndent result.add p.indentLine(def) - result.add p.indentLine(~"}$n") + result.add p.indentLine("}\n") #if gVerbosity >= 3: # echo "END generated code for: " & prc.name.s @@ -2475,7 +2548,7 @@ proc genProc(oldProc: PProc, prc: PSym): Rope = proc genStmt(p: PProc, n: PNode) = var r: TCompRes gen(p, n, r) - if r.res != nil: lineF(p, "$#;$n", [r.res]) + if r.res != "": lineF(p, "$#;$n", [r.res]) proc genPragma(p: PProc, n: PNode) = for it in n.sons: @@ -2515,7 +2588,7 @@ proc genCast(p: PProc, n: PNode, r: var TCompRes) = r.res = "($1 - ($2 $3))" % [rope minuend, r.res, trimmer] elif (src.kind == tyPtr and mapType(p, src) == etyObject) and dest.kind == tyPointer: r.address = r.res - r.res = ~"null" + r.res = "null" r.typ = etyBaseIndex elif (dest.kind == tyPtr and mapType(p, dest) == etyObject) and src.kind == tyPointer: r.res = r.address @@ -2524,8 +2597,8 @@ proc genCast(p: PProc, n: PNode, r: var TCompRes) = proc gen(p: PProc, n: PNode, r: var TCompRes) = r.typ = etyNone if r.kind != resCallee: r.kind = resNone - #r.address = nil - r.res = nil + #r.address = "" + r.res = "" case n.kind of nkSym: @@ -2549,11 +2622,11 @@ proc gen(p: PProc, n: PNode, r: var TCompRes) = r.kind = resExpr of nkStrLit..nkTripleStrLit: if skipTypes(n.typ, abstractVarRange).kind == tyString: - if n.strVal.len != 0: + if n.strVal.len <= 64: + r.res = makeJsNimStrLit(n.strVal) + else: useMagic(p, "makeNimstrLit") r.res = "makeNimstrLit($1)" % [makeJSString(n.strVal)] - else: - r.res = rope"[]" else: r.res = makeJSString(n.strVal, false) r.kind = resExpr @@ -2617,7 +2690,7 @@ proc gen(p: PProc, n: PNode, r: var TCompRes) = let s = n[namePos].sym discard mangleName(p.module, s) r.res = s.loc.r - if lfNoDecl in s.loc.flags or s.magic notin {mNone, mIsolate}: discard + if lfNoDecl in s.loc.flags or s.magic notin generatedMagics: discard elif not p.g.generatedSyms.containsOrIncl(s.id): p.locals.add(genProc(p, s)) of nkType: r.res = genTypeInfo(p, n.typ) @@ -2664,7 +2737,7 @@ proc gen(p: PProc, n: PNode, r: var TCompRes) = var s = n[namePos].sym if {sfExportc, sfCompilerProc} * s.flags == {sfExportc}: genSym(p, n[namePos], r) - r.res = nil + r.res = "" of nkGotoState, nkState: globalError(p.config, n.info, "First class iterators not implemented") of nkPragmaBlock: gen(p, n.lastSon, r) @@ -2673,6 +2746,7 @@ proc gen(p: PProc, n: PNode, r: var TCompRes) = else: internalError(p.config, n.info, "gen: unknown node type: " & $n.kind) proc newModule(g: ModuleGraph; module: PSym): BModule = + ## Create a new JS backend module node. new(result) result.module = module result.sigConflicts = initCountTable[SigHash]() @@ -2684,6 +2758,7 @@ proc newModule(g: ModuleGraph; module: PSym): BModule = PGlobals(g.backend).inSystem = true proc genHeader(): Rope = + ## Generate the JS header. result = rope("""/* Generated by the Nim Compiler v$1 */ var framePtr = null; var excHandler = 0; @@ -2714,6 +2789,8 @@ proc addHcrInitGuards(p: PProc, n: PNode, genStmt(p, n) proc genModule(p: PProc, n: PNode) = + ## Generate the JS module code. + ## Called for each top level node in a Nim module. if optStackTrace in p.options: p.body.add(frameCreate(p, makeJSString("module " & p.module.module.name.s), @@ -2742,6 +2819,7 @@ proc genModule(p: PProc, n: PNode) = p.body.add(frameDestroy(p)) proc myProcess(b: PPassContext, n: PNode): PNode = + ## Generate JS code for a node. result = n let m = BModule(b) if passes.skipCodegen(m.config, n): return n @@ -2754,6 +2832,7 @@ proc myProcess(b: PPassContext, n: PNode): PNode = p.g.code.add(p.body) proc wholeCode(graph: ModuleGraph; m: BModule): Rope = + ## Combine source code from all nodes. let globals = PGlobals(graph.backend) for prc in globals.forwarded: if not globals.generatedSyms.containsOrIncl(prc.id): @@ -2775,32 +2854,45 @@ proc getClassName(t: PType): Rope = s = skipTypes(t, abstractPtrs).sym if s.isNil or sfAnon in s.flags: doAssert(false, "cannot retrieve class name") - if s.loc.r != nil: result = s.loc.r + if s.loc.r != "": result = s.loc.r else: result = rope(s.name.s) proc myClose(graph: ModuleGraph; b: PPassContext, n: PNode): PNode = - result = myProcess(b, n) + ## Finalize JS code generation of a Nim module. + ## Param `n` may contain nodes returned from the last module close call. var m = BModule(b) if sfMainModule in m.module.flags: - for destructorCall in graph.globalDestructors: - n.add destructorCall + # Add global destructors to the module. + # This must come before the last call to `myProcess`. + for i in countdown(high(graph.globalDestructors), 0): + n.add graph.globalDestructors[i] + # Process any nodes left over from the last call to `myClose`. + result = myProcess(b, n) + # Some codegen is different (such as no stacktraces; see `initProcOptions`) + # when `std/system` is being processed. if sfSystemModule in m.module.flags: PGlobals(graph.backend).inSystem = false + # Check if codegen should continue before any files are generated. + # It may bail early is if too many errors have been raised. if passes.skipCodegen(m.config, n): return n + # Nim modules are compiled into a single JS file. + # If this is the main module, then this is the final call to `myClose`. if sfMainModule in m.module.flags: var code = genHeader() & wholeCode(graph, m) let outFile = m.config.prepareToWriteOutput() - + # Generate an optional source map. if optSourcemap in m.config.globalOptions: var map: SourceMap (code, map) = genSourceMap($(code), outFile.string) writeFile(outFile.string & ".map", $(%map)) + # Check if the generated JS code matches the output file, or else + # write it to the file. if not equalsFile(code, outFile): if not writeRope(code, outFile): rawMessage(m.config, errCannotOpenFile, outFile.string) - proc myOpen(graph: ModuleGraph; s: PSym; idgen: IdGenerator): PPassContext = + ## Create the JS backend pass context `BModule` for a Nim module. result = newModule(graph, s) result.idgen = idgen diff --git a/compiler/jstypes.nim b/compiler/jstypes.nim index 5b684b60c0b2..56d075af72b8 100644 --- a/compiler/jstypes.nim +++ b/compiler/jstypes.nim @@ -19,13 +19,13 @@ proc genObjectFields(p: PProc, typ: PType, n: PNode): Rope = s, u: Rope field: PSym b: PNode - result = nil + result = "" case n.kind of nkRecList: if n.len == 1: result = genObjectFields(p, typ, n[0]) else: - s = nil + s = "" for i in 0.. 0: s.add(", \L") s.add(genObjectFields(p, typ, n[i])) @@ -44,13 +44,13 @@ proc genObjectFields(p: PProc, typ: PType, n: PNode): Rope = s = genTypeInfo(p, field.typ) for i in 1.. 0: s.add(", \L") s.addf("{kind: 1, offset: \"Field$1\", len: 0, " & @@ -102,7 +102,7 @@ proc genTupleInfo(p: PProc, typ: PType, name: Rope) = p.g.typeInfo.addf("$1.node = NNI$2;$n", [name, rope(typ.id)]) proc genEnumInfo(p: PProc, typ: PType, name: Rope) = - var s: Rope = nil + var s: Rope = "" for i in 0.. which cannot be captured as it would violate memory" & - " safety, declared here: $3; using '-d:nimNoLentIterators' helps in some cases") % + " safety, declared here: $3; using '-d:nimNoLentIterators' helps in some cases." & + " Consider using a which can be captured.") % [s.name.s, typeToString(s.typ), g.config$s.info]) elif not (owner.typ.callConv == ccClosure or owner.typ.callConv == ccNimCall and tfExplicitCallConv notin owner.typ.flags): localError(g.config, n.info, "illegal capture '$1' because '$2' has the calling convention: <$3>" % diff --git a/compiler/lexer.nim b/compiler/lexer.nim index e795d52c0802..dabb1c8fb0c6 100644 --- a/compiler/lexer.nim +++ b/compiler/lexer.nim @@ -20,7 +20,7 @@ import wordrecg, lineinfos, pathutils, parseutils when defined(nimPreviewSlimSystem): - import std/assertions + import std/[assertions, formatfloat] const MaxLineLength* = 80 # lines longer than this lead to a warning @@ -505,11 +505,11 @@ proc getNumber(L: var Lexer, result: var Token) = of tkUInt16Lit: setNumber result.iNumber, xi and 0xffff of tkUInt32Lit: setNumber result.iNumber, xi and 0xffffffff of tkFloat32Lit: - setNumber result.fNumber, (cast[PFloat32](addr(xi)))[] + setNumber result.fNumber, (cast[ptr float32](addr(xi)))[] # note: this code is endian neutral! # XXX: Test this on big endian machine! of tkFloat64Lit, tkFloatLit: - setNumber result.fNumber, (cast[PFloat64](addr(xi)))[] + setNumber result.fNumber, (cast[ptr float64](addr(xi)))[] else: internalError(L.config, getLineInfo(L), "getNumber") # Bounds checks. Non decimal literals are allowed to overflow the range of @@ -907,7 +907,7 @@ proc getSymbol(L: var Lexer, tok: var Token) = inc(pos) suspicious = true of '\x80'..'\xFF': - if c in UnicodeOperatorStartChars and unicodeOperators in L.config.features and unicodeOprLen(L.buf, pos)[0] != 0: + if c in UnicodeOperatorStartChars and unicodeOprLen(L.buf, pos)[0] != 0: break else: h = h !& ord(c) @@ -943,7 +943,7 @@ proc getOperator(L: var Lexer, tok: var Token) = if c in OpChars: h = h !& ord(c) inc(pos) - elif c in UnicodeOperatorStartChars and unicodeOperators in L.config.features: + elif c in UnicodeOperatorStartChars: let oprLen = unicodeOprLen(L.buf, pos)[0] if oprLen == 0: break for i in 0.. 1 and tok.ident.s[^1] == '>' and - tok.ident.s[^2] in {'-', '~', '='}: return 1 + tok.ident.s[^2] in {'-', '~', '='}: return 0 template considerAsgn(value: untyped) = result = if tok.ident.s[^1] == '=': 1 else: value @@ -1244,7 +1244,7 @@ proc rawGetTok*(L: var Lexer, tok: var Token) = else: case c of UnicodeOperatorStartChars: - if unicodeOperators in L.config.features and unicodeOprLen(L.buf, L.bufpos)[0] != 0: + if unicodeOprLen(L.buf, L.bufpos)[0] != 0: getOperator(L, tok) else: getSymbol(L, tok) @@ -1355,7 +1355,7 @@ proc rawGetTok*(L: var Lexer, tok: var Token) = getNumber(L, tok) let c = L.buf[L.bufpos] if c in SymChars+{'_'}: - if c in UnicodeOperatorStartChars and unicodeOperators in L.config.features and + if c in UnicodeOperatorStartChars and unicodeOprLen(L.buf, L.bufpos)[0] != 0: discard else: @@ -1370,7 +1370,7 @@ proc rawGetTok*(L: var Lexer, tok: var Token) = getNumber(L, tok) let c = L.buf[L.bufpos] if c in SymChars+{'_'}: - if c in UnicodeOperatorStartChars and unicodeOperators in L.config.features and + if c in UnicodeOperatorStartChars and unicodeOprLen(L.buf, L.bufpos)[0] != 0: discard else: diff --git a/compiler/liftdestructors.nim b/compiler/liftdestructors.nim index 68c93a179de0..5174a908feef 100644 --- a/compiler/liftdestructors.nim +++ b/compiler/liftdestructors.nim @@ -165,9 +165,12 @@ proc fillBodyObj(c: var TLiftCtx; n, body, x, y: PNode; enforceDefaultOp: bool) # the value needs to be destroyed before we assign the selector # or the value is lost let prevKind = c.kind + let prevAddMemReset = c.addMemReset c.kind = attachedDestructor + c.addMemReset = true fillBodyObj(c, n, body, x, y, enforceDefaultOp = false) c.kind = prevKind + c.addMemReset = prevAddMemReset localEnforceDefaultOp = true if c.kind != attachedDestructor: diff --git a/compiler/lineinfos.nim b/compiler/lineinfos.nim index 575be6196589..ec16439603eb 100644 --- a/compiler/lineinfos.nim +++ b/compiler/lineinfos.nim @@ -32,8 +32,10 @@ type # non-fatal errors errIllFormedAstX, errCannotOpenFile, errXExpected, + errRstMissingClosing, errRstGridTableNotImplemented, errRstMarkdownIllformedTable, + errRstIllformedTable, errRstNewSectionExpected, errRstGeneralParseError, errRstInvalidDirectiveX, @@ -78,6 +80,8 @@ type warnHoleEnumConv = "HoleEnumConv", warnCstringConv = "CStringConv", warnEffect = "Effect", + warnCastSizes = "CastSizes" + warnTemplateRedefinition = "TemplateRedefinition", warnUser = "User", # hints hintSuccess = "Success", hintSuccessX = "SuccessX", @@ -104,8 +108,10 @@ const errIllFormedAstX: "illformed AST: $1", errCannotOpenFile: "cannot open '$1'", errXExpected: "'$1' expected", + errRstMissingClosing: "$1", errRstGridTableNotImplemented: "grid table is not implemented", errRstMarkdownIllformedTable: "illformed delimiter row of a markdown table", + errRstIllformedTable: "Illformed table: $1", errRstNewSectionExpected: "new section expected $1", errRstGeneralParseError: "general parse error", errRstInvalidDirectiveX: "invalid directive: '$1'", @@ -169,6 +175,8 @@ const warnHoleEnumConv: "$1", warnCstringConv: "$1", warnEffect: "$1", + warnCastSizes: "$1", + warnTemplateRedefinition: "template '$1' is implicitly redefined, consider adding an explicit .redefine pragma", warnUser: "$1", hintSuccess: "operation successful: $#", # keep in sync with `testament.isSuccess` diff --git a/compiler/linter.nim b/compiler/linter.nim index 5fb646051a2e..0c2aaef79215 100644 --- a/compiler/linter.nim +++ b/compiler/linter.nim @@ -12,7 +12,8 @@ import std/strutils from std/sugar import dup -import options, ast, msgs, idents, lineinfos, wordrecg, astmsgs +import options, ast, msgs, idents, lineinfos, wordrecg, astmsgs, semdata, packages +export packages const Letters* = {'a'..'z', 'A'..'Z', '0'..'9', '\x80'..'\xFF', '_'} @@ -85,24 +86,33 @@ proc differ*(line: string, a, b: int, x: string): string = result = y proc nep1CheckDefImpl(conf: ConfigRef; info: TLineInfo; s: PSym; k: TSymKind) = - # operators stay as they are: - if k in {skResult, skTemp} or s.name.s[0] notin Letters: return - if k in {skType, skGenericParam} and sfAnon in s.flags: return - if s.typ != nil and s.typ.kind == tyTypeDesc: return - if {sfImportc, sfExportc} * s.flags != {}: return - if optStyleCheck notin s.options: return let beau = beautifyName(s.name.s, k) if s.name.s != beau: lintReport(conf, info, beau, s.name.s) -template styleCheckDef*(conf: ConfigRef; info: TLineInfo; s: PSym; k: TSymKind) = - if {optStyleHint, optStyleError} * conf.globalOptions != {} and optStyleUsages notin conf.globalOptions: - nep1CheckDefImpl(conf, info, s, k) - -template styleCheckDef*(conf: ConfigRef; info: TLineInfo; s: PSym) = - styleCheckDef(conf, info, s, s.kind) -template styleCheckDef*(conf: ConfigRef; s: PSym) = - styleCheckDef(conf, s.info, s, s.kind) +template styleCheckDef*(ctx: PContext; info: TLineInfo; sym: PSym; k: TSymKind) = + ## Check symbol definitions adhere to NEP1 style rules. + if optStyleCheck in ctx.config.options and # ignore if styleChecks are off + {optStyleHint, optStyleError} * ctx.config.globalOptions != {} and # check only if hint/error is enabled + hintName in ctx.config.notes and # ignore if name checks are not requested + ctx.config.belongsToProjectPackage(ctx.module) and # ignore foreign packages + optStyleUsages notin ctx.config.globalOptions and # ignore if requested to only check name usage + sym.kind != skResult and # ignore `result` + sym.kind != skTemp and # ignore temporary variables created by the compiler + sym.name.s[0] in Letters and # ignore operators TODO: what about unicode symbols??? + k notin {skType, skGenericParam} and # ignore types and generic params + (sym.typ == nil or sym.typ.kind != tyTypeDesc) and # ignore `typedesc` + {sfImportc, sfExportc} * sym.flags == {} and # ignore FFI + sfAnon notin sym.flags: # ignore if created by compiler + nep1CheckDefImpl(ctx.config, info, sym, k) + +template styleCheckDef*(ctx: PContext; info: TLineInfo; s: PSym) = + ## Check symbol definitions adhere to NEP1 style rules. + styleCheckDef(ctx, info, s, s.kind) + +template styleCheckDef*(ctx: PContext; s: PSym) = + ## Check symbol definitions adhere to NEP1 style rules. + styleCheckDef(ctx, s.info, s, s.kind) proc differs(conf: ConfigRef; info: TLineInfo; newName: string): string = let line = sourceLine(conf, info) @@ -116,23 +126,27 @@ proc differs(conf: ConfigRef; info: TLineInfo; newName: string): string = let last = first+identLen(line, first)-1 result = differ(line, first, last, newName) -proc styleCheckUse*(conf: ConfigRef; info: TLineInfo; s: PSym) = - if info.fileIndex.int < 0: return - # we simply convert it to what it looks like in the definition - # for consistency - - # operators stay as they are: - if s.kind == skTemp or s.name.s[0] notin Letters or sfAnon in s.flags: - return - +proc styleCheckUseImpl(conf: ConfigRef; info: TLineInfo; s: PSym) = let newName = s.name.s let badName = differs(conf, info, newName) if badName.len > 0: - # special rules for historical reasons - let forceHint = badName == "nnkArgList" and newName == "nnkArglist" or badName == "nnkArglist" and newName == "nnkArgList" - lintReport(conf, info, newName, badName, forceHint = forceHint, extraMsg = "".dup(addDeclaredLoc(conf, s))) - -proc checkPragmaUse*(conf: ConfigRef; info: TLineInfo; w: TSpecialWord; pragmaName: string) = + lintReport(conf, info, newName, badName, "".dup(addDeclaredLoc(conf, s))) + +template styleCheckUse*(ctx: PContext; info: TLineInfo; sym: PSym) = + ## Check symbol uses match their definition's style. + if {optStyleHint, optStyleError} * ctx.config.globalOptions != {} and # ignore if styleChecks are off + hintName in ctx.config.notes and # ignore if name checks are not requested + ctx.config.belongsToProjectPackage(ctx.module) and # ignore foreign packages + sym.kind != skTemp and # ignore temporary variables created by the compiler + sym.name.s[0] in Letters and # ignore operators TODO: what about unicode symbols??? + sfAnon notin sym.flags: # ignore temporary variables created by the compiler + styleCheckUseImpl(ctx.config, info, sym) + +proc checkPragmaUseImpl(conf: ConfigRef; info: TLineInfo; w: TSpecialWord; pragmaName: string) = let wanted = $w if pragmaName != wanted: lintReport(conf, info, wanted, pragmaName) + +template checkPragmaUse*(conf: ConfigRef; info: TLineInfo; w: TSpecialWord; pragmaName: string) = + if {optStyleHint, optStyleError} * conf.globalOptions != {}: + checkPragmaUseImpl(conf, info, w, pragmaName) diff --git a/compiler/lookups.nim b/compiler/lookups.nim index d61e15915ad3..63e4920f7a1c 100644 --- a/compiler/lookups.nim +++ b/compiler/lookups.nim @@ -182,8 +182,6 @@ iterator allSyms*(c: PContext): (PSym, int, bool) = proc someSymFromImportTable*(c: PContext; name: PIdent; ambiguous: var bool): PSym = var marked = initIntSet() var symSet = OverloadableSyms - if overloadableEnums notin c.features: - symSet.excl skEnumField result = nil block outer: for im in c.imports.mitems: diff --git a/compiler/lowerings.nim b/compiler/lowerings.nim index de2f678d70d5..fc66fc9fa20d 100644 --- a/compiler/lowerings.nim +++ b/compiler/lowerings.nim @@ -241,7 +241,8 @@ proc addField*(obj: PType; s: PSym; cache: IdentCache; idgen: IdGenerator): PSym assert t.kind != tyTyped propagateToOwner(obj, t) field.position = obj.n.len - field.flags = s.flags * {sfCursor} + # sfNoInit flag for skField is used in closureiterator codegen + field.flags = s.flags * {sfCursor, sfNoInit} obj.n.add newSymNode(field) fieldCheck() result = field diff --git a/compiler/main.nim b/compiler/main.nim index a4425e510c92..171521c7a9b6 100644 --- a/compiler/main.nim +++ b/compiler/main.nim @@ -276,7 +276,8 @@ proc mainCommand*(graph: ModuleGraph) = var ret = if optUseNimcache in conf.globalOptions: getNimcacheDir(conf) else: conf.projectPath doAssert ret.string.isAbsolute # `AbsoluteDir` is not a real guarantee - if conf.cmd in cmdDocLike + {cmdRst2html, cmdRst2tex}: ret = ret / htmldocsDir + if conf.cmd in cmdDocLike + {cmdRst2html, cmdRst2tex, cmdMd2html, cmdMd2tex}: + ret = ret / htmldocsDir conf.outDir = ret ## process all commands @@ -302,7 +303,7 @@ proc mainCommand*(graph: ModuleGraph) = commandDoc2(graph, HtmlExt) if optGenIndex in conf.globalOptions and optWholeProject in conf.globalOptions: commandBuildIndex(conf, $conf.outDir) - of cmdRst2html: + of cmdRst2html, cmdMd2html: # XXX: why are warnings disabled by default for rst2html and rst2tex? for warn in rstWarnings: conf.setNoteDefaults(warn, true) @@ -311,20 +312,24 @@ proc mainCommand*(graph: ModuleGraph) = conf.quitOrRaise "compiler wasn't built with documentation generator" else: loadConfigs(DocConfig, cache, conf, graph.idgen) - commandRst2Html(cache, conf) - of cmdRst2tex, cmdDoc2tex: + commandRst2Html(cache, conf, preferMarkdown = (conf.cmd == cmdMd2html)) + of cmdRst2tex, cmdMd2tex, cmdDoc2tex: for warn in rstWarnings: conf.setNoteDefaults(warn, true) when defined(leanCompiler): conf.quitOrRaise "compiler wasn't built with documentation generator" else: - if conf.cmd == cmdRst2tex: + if conf.cmd in {cmdRst2tex, cmdMd2tex}: loadConfigs(DocTexConfig, cache, conf, graph.idgen) - commandRst2TeX(cache, conf) + commandRst2TeX(cache, conf, preferMarkdown = (conf.cmd == cmdMd2tex)) else: docLikeCmd commandDoc2(graph, TexExt) of cmdJsondoc0: docLikeCmd commandJson(cache, conf) - of cmdJsondoc: docLikeCmd commandDoc2(graph, JsonExt) + of cmdJsondoc: + docLikeCmd(): + commandDoc2(graph, JsonExt) + if optGenIndex in conf.globalOptions and optWholeProject in conf.globalOptions: + commandBuildIndexJson(conf, $conf.outDir) of cmdCtags: docLikeCmd commandTags(cache, conf) of cmdBuildindex: docLikeCmd commandBuildIndex(conf, $conf.projectFull, conf.outFile) of cmdGendepend: commandGenDepend(graph) diff --git a/compiler/modulegraphs.nim b/compiler/modulegraphs.nim index c44908dc33bb..0df72c43b236 100644 --- a/compiler/modulegraphs.nim +++ b/compiler/modulegraphs.nim @@ -11,8 +11,8 @@ ## represents a complete Nim project. Single modules can either be kept in RAM ## or stored in a rod-file. -import intsets, tables, hashes, md5_old -import ast, astalgo, options, lineinfos,idents, btrees, ropes, msgs, pathutils +import intsets, tables, hashes, md5_old, sequtils +import ast, astalgo, options, lineinfos,idents, btrees, ropes, msgs, pathutils, packages import ic / [packed_ast, ic] when defined(nimPreviewSlimSystem): @@ -53,6 +53,10 @@ type concreteTypes*: seq[FullId] inst*: PInstantiation + SymInfoPair* = object + sym*: PSym + info*: TLineInfo + ModuleGraph* {.acyclic.} = ref object ifaces*: seq[Iface] ## indexed by int32 fileIdx packed*: PackedModuleGraph @@ -67,7 +71,6 @@ type startupPackedConfig*: PackedConfig packageSyms*: TStrTable - modulesPerPackage*: Table[ItemId, TStrTable] deps*: IntSet # the dependency graph or potentially its transitive closure. importDeps*: Table[FileIndex, seq[FileIndex]] # explicit import module dependencies suggestMode*: bool # whether we are in nimsuggest mode or not. @@ -84,6 +87,8 @@ type doStopCompile*: proc(): bool {.closure.} usageSym*: PSym # for nimsuggest owners*: seq[PSym] + suggestSymbols*: Table[FileIndex, seq[SymInfoPair]] + suggestErrors*: Table[FileIndex, seq[Suggest]] methods*: seq[tuple[methods: seq[PSym], dispatcher: PSym]] # needs serialization! systemModule*: PSym sysTypes*: array[TTypeKind, PType] @@ -371,20 +376,9 @@ template getPContext(): untyped = when c is PContext: c else: c.c -when defined(nimfind): - template onUse*(info: TLineInfo; s: PSym) = - let c = getPContext() - if c.graph.onUsage != nil: c.graph.onUsage(c.graph, s, info) - - template onDef*(info: TLineInfo; s: PSym) = - let c = getPContext() - if c.graph.onDefinition != nil: c.graph.onDefinition(c.graph, s, info) - - template onDefResolveForward*(info: TLineInfo; s: PSym) = - let c = getPContext() - if c.graph.onDefinitionResolveForward != nil: - c.graph.onDefinitionResolveForward(c.graph, s, info) - +when defined(nimsuggest): + template onUse*(info: TLineInfo; s: PSym) = discard + template onDefResolveForward*(info: TLineInfo; s: PSym) = discard else: template onUse*(info: TLineInfo; s: PSym) = discard template onDef*(info: TLineInfo; s: PSym) = discard @@ -435,8 +429,7 @@ proc initOperators*(g: ModuleGraph): Operators = result.opNot = createMagic(g, "not", mNot) result.opContains = createMagic(g, "contains", mInSet) -proc newModuleGraph*(cache: IdentCache; config: ConfigRef): ModuleGraph = - result = ModuleGraph() +proc initModuleGraphFields(result: ModuleGraph) = # A module ID of -1 means that the symbol is not attached to a module at all, # but to the module graph: result.idgen = IdGenerator(module: -1'i32, symId: 0'i32, typeId: 0'i32) @@ -446,9 +439,9 @@ proc newModuleGraph*(cache: IdentCache; config: ConfigRef): ModuleGraph = result.ifaces = @[] result.importStack = @[] result.inclToMod = initTable[FileIndex, FileIndex]() - result.config = config - result.cache = cache result.owners = @[] + result.suggestSymbols = initTable[FileIndex, seq[SymInfoPair]]() + result.suggestErrors = initTable[FileIndex, seq[Suggest]]() result.methods = @[] initStrTable(result.compilerprocs) initStrTable(result.exposed) @@ -462,6 +455,12 @@ proc newModuleGraph*(cache: IdentCache; config: ConfigRef): ModuleGraph = result.operators = initOperators(result) result.emittedTypeInfo = initTable[string, FileIndex]() +proc newModuleGraph*(cache: IdentCache; config: ConfigRef): ModuleGraph = + result = ModuleGraph() + result.config = config + result.cache = cache + initModuleGraphFields(result) + proc resetAllModules*(g: ModuleGraph) = initStrTable(g.packageSyms) g.deps = initIntSet() @@ -473,6 +472,7 @@ proc resetAllModules*(g: ModuleGraph) = g.methods = @[] initStrTable(g.compilerprocs) initStrTable(g.exposed) + initModuleGraphFields(g) proc getModule*(g: ModuleGraph; fileIdx: FileIndex): PSym = if fileIdx.int32 >= 0: @@ -551,7 +551,19 @@ proc transitiveClosure(g: var IntSet; n: int) = proc markDirty*(g: ModuleGraph; fileIdx: FileIndex) = let m = g.getModule fileIdx - if m != nil: incl m.flags, sfDirty + if m != nil: + g.suggestSymbols.del(fileIdx) + g.suggestErrors.del(fileIdx) + incl m.flags, sfDirty + +proc unmarkAllDirty*(g: ModuleGraph) = + for i in 0i32.. pkg.len+1 and - p[pkg.len] == '-' and p.startsWith(pkg): - let (_, a, _) = getPathVersionChecksum(p) - if bestv.len == 0 or bestv < a: - bestv = a - best = dir / p - - if best.len > 0: - var f: File - if open(f, best / changeFileExt(pkg, ".nimble-link")): - # the second line contains what we're interested in, see: - # https://github.com/nim-lang/nimble#nimble-link - var override = "" - discard readLine(f, override) - discard readLine(f, override) - close(f) - if not override.isAbsolute(): - best = best / override - else: - best = override - let f = if subdir.len == 0: pkg else: subdir - let res = addFileExt(best / f, "nim") - if best.len > 0 and fileExists(res): - result = res - -when false: - proc resolveDollar(project, source, pkg, subdir: string; info: TLineInfo): string = - template attempt(a) = - let x = addFileExt(a, "nim") - if fileExists(x): return x - - case pkg - of "stdlib": - if subdir.len == 0: - return options.libpath - else: - for candidate in stdlibDirs: - attempt(options.libpath / candidate / subdir) - of "root": - let root = project.splitFile.dir - if subdir.len == 0: - return root - else: - attempt(root / subdir) - else: - when considerParentDirs: - var p = parentDir(source.splitFile.dir) - # support 'import $karax': - let f = if subdir.len == 0: pkg else: subdir - - while p.len > 0: - let dir = p / pkg - if dirExists(dir): - attempt(dir / f) - # 2nd attempt: try to use 'karax/karax' - attempt(dir / pkg / f) - # 3rd attempt: try to use 'karax/src/karax' - attempt(dir / "src" / f) - attempt(dir / "src" / pkg / f) - p = parentDir(p) - - when considerNimbleDirs: - if not options.gNoNimblePath: - var nimbleDir = getEnv("NIMBLE_DIR") - if nimbleDir.len == 0: nimbleDir = getHomeDir() / ".nimble" - result = findInNimbleDir(pkg, subdir, nimbleDir / "pkgs") - if result.len > 0: return result - when not defined(windows): - result = findInNimbleDir(pkg, subdir, "/opt/nimble/pkgs") - if result.len > 0: return result - - proc scriptableImport(pkg, sub: string; info: TLineInfo): string = - resolveDollar(gProjectFull, info.toFullPath(), pkg, sub, info) - - proc lookupPackage(pkg, subdir: PNode): string = - let sub = if subdir != nil: renderTree(subdir, {renderNoComments}).replace(" ") else: "" - case pkg.kind - of nkStrLit, nkRStrLit, nkTripleStrLit: - result = scriptableImport(pkg.strVal, sub, pkg.info) - of nkIdent: - result = scriptableImport(pkg.ident.s, sub, pkg.info) - else: - localError(pkg.info, "package name must be an identifier or string literal") - result = "" - proc getModuleName*(conf: ConfigRef; n: PNode): string = # This returns a short relative module name without the nim extension # e.g. like "system", "importer" or "somepath/module" @@ -163,3 +69,18 @@ proc checkModuleName*(conf: ConfigRef; n: PNode; doLocalError=true): FileIndex = result = InvalidFileIdx else: result = fileInfoIdx(conf, fullPath) + +proc mangleModuleName*(conf: ConfigRef; path: AbsoluteFile): string = + ## Mangle a relative module path to avoid path and symbol collisions. + ## + ## Used by backends that need to generate intermediary files from Nim modules. + ## This is needed because the compiler uses a flat cache file hierarchy. + ## + ## Example: + ## `foo-#head/../bar` becomes `@foo-@hhead@s..@sbar` + "@m" & relativeTo(path, conf.projectPath).string.multiReplace( + {$os.DirSep: "@s", $os.AltSep: "@s", "#": "@h", "@": "@@", ":": "@c"}) + +proc demangleModuleName*(path: string): string = + ## Demangle a relative module path. + result = path.multiReplace({"@@": "@", "@h": "#", "@s": "/", "@m": "", "@c": ":"}) diff --git a/compiler/modules.nim b/compiler/modules.nim index dd5db63fae2d..2becef38f933 100644 --- a/compiler/modules.nim +++ b/compiler/modules.nim @@ -12,7 +12,7 @@ import ast, astalgo, magicsys, msgs, options, idents, lexer, passes, syntaxes, llstream, modulegraphs, - lineinfos, pathutils, tables + lineinfos, pathutils, tables, packages when defined(nimPreviewSlimSystem): import std/[syncio, assertions] @@ -25,56 +25,11 @@ proc resetSystemArtifacts*(g: ModuleGraph) = template getModuleIdent(graph: ModuleGraph, filename: AbsoluteFile): PIdent = getIdent(graph.cache, splitFile(filename).name) -template packageId(): untyped {.dirty.} = ItemId(module: PackageModuleId, item: int32(fileIdx)) - -proc getPackage(graph: ModuleGraph; fileIdx: FileIndex): PSym = - ## returns package symbol (skPackage) for yet to be defined module for fileIdx - let filename = AbsoluteFile toFullPath(graph.config, fileIdx) - let name = getModuleIdent(graph, filename) - let info = newLineInfo(fileIdx, 1, 1) - let - pck = getPackageName(graph.config, filename.string) - pck2 = if pck.len > 0: pck else: "unknown" - pack = getIdent(graph.cache, pck2) - result = graph.packageSyms.strTableGet(pack) - if result == nil: - result = newSym(skPackage, getIdent(graph.cache, pck2), packageId(), nil, info) - #initStrTable(packSym.tab) - graph.packageSyms.strTableAdd(result) - else: - let modules = graph.modulesPerPackage.getOrDefault(result.itemId) - let existing = if modules.data.len > 0: strTableGet(modules, name) else: nil - if existing != nil and existing.info.fileIndex != info.fileIndex: - when false: - # we used to produce an error: - localError(graph.config, info, - "module names need to be unique per Nimble package; module clashes with " & - toFullPath(graph.config, existing.info.fileIndex)) - else: - # but starting with version 0.20 we now produce a fake Nimble package instead - # to resolve the conflicts: - let pck3 = fakePackageName(graph.config, filename) - # this makes the new `result`'s owner be the original `result` - result = newSym(skPackage, getIdent(graph.cache, pck3), packageId(), result, info) - #initStrTable(packSym.tab) - graph.packageSyms.strTableAdd(result) - proc partialInitModule(result: PSym; graph: ModuleGraph; fileIdx: FileIndex; filename: AbsoluteFile) = let packSym = getPackage(graph, fileIdx) result.owner = packSym result.position = int fileIdx - #initStrTable(result.tab(graph)) - when false: - strTableAdd(result.tab, result) # a module knows itself - # This is now implemented via - # c.moduleScope.addSym(module) # a module knows itself - # in sem.nim, around line 527 - - if graph.modulesPerPackage.getOrDefault(packSym.itemId).data.len == 0: - graph.modulesPerPackage[packSym.itemId] = newStrTable() - graph.modulesPerPackage[packSym.itemId].strTableAdd(result) - proc newModule(graph: ModuleGraph; fileIdx: FileIndex): PSym = let filename = AbsoluteFile toFullPath(graph.config, fileIdx) # We cannot call ``newSym`` here, because we have to circumvent the ID @@ -136,7 +91,7 @@ proc importModule*(graph: ModuleGraph; s: PSym, fileIdx: FileIndex): PSym = # localError(result.info, errAttemptToRedefine, result.name.s) # restore the notes for outer module: graph.config.notes = - if s.getnimblePkgId == graph.config.mainPackageId or isDefined(graph.config, "booting"): graph.config.mainPackageNotes + if graph.config.belongsToProjectPackage(s) or isDefined(graph.config, "booting"): graph.config.mainPackageNotes else: graph.config.foreignPackageNotes proc includeModule*(graph: ModuleGraph; s: PSym, fileIdx: FileIndex): PNode = @@ -171,7 +126,7 @@ proc compileProject*(graph: ModuleGraph; projectFileIdx = InvalidFileIdx) = conf.projectMainIdx2 = projectFile let packSym = getPackage(graph, projectFile) - graph.config.mainPackageId = packSym.getnimblePkgId + graph.config.mainPackageId = packSym.getPackageId graph.importStack.add projectFile if projectFile == systemFileIdx: diff --git a/compiler/msgs.nim b/compiler/msgs.nim index 9d111b2e2430..c7610b630a24 100644 --- a/compiler/msgs.nim +++ b/compiler/msgs.nim @@ -10,7 +10,9 @@ import std/[strutils, os, tables, terminal, macros, times], std/private/miscdollars, - options, ropes, lineinfos, pathutils, strutils2 + options, lineinfos, pathutils, strutils2 + +import ropes except `%` when defined(nimPreviewSlimSystem): import std/[syncio, assertions] @@ -43,18 +45,16 @@ proc toCChar*(c: char; result: var string) {.inline.} = result.add c proc makeCString*(s: string): Rope = - result = nil - var res = newStringOfCap(int(s.len.toFloat * 1.1) + 1) - res.add("\"") + result = newStringOfCap(int(s.len.toFloat * 1.1) + 1) + result.add("\"") for i in 0..= 0 - shallowCopy(conf.m.fileInfos[fileIdx.int32].hash, hash) + when defined(gcArc) or defined(gcOrc): + conf.m.fileInfos[fileIdx.int32].hash = hash + else: + shallowCopy(conf.m.fileInfos[fileIdx.int32].hash, hash) + proc getHash*(conf: ConfigRef; fileIdx: FileIndex): string = assert fileIdx.int32 >= 0 - shallowCopy(result, conf.m.fileInfos[fileIdx.int32].hash) + when defined(gcArc) or defined(gcOrc): + result = conf.m.fileInfos[fileIdx.int32].hash + else: + shallowCopy(result, conf.m.fileInfos[fileIdx.int32].hash) proc toFullPathConsiderDirty*(conf: ConfigRef; fileIdx: FileIndex): AbsoluteFile = if fileIdx.int32 < 0: @@ -411,12 +419,12 @@ To create a stacktrace, rerun compilation with './koch temp $1 ', see $2 f [conf.command, "intern.html#debugging-the-compiler".createDocLink], conf.unitSep) quit 1 -proc handleError(conf: ConfigRef; msg: TMsgKind, eh: TErrorHandling, s: string) = +proc handleError(conf: ConfigRef; msg: TMsgKind, eh: TErrorHandling, s: string, ignoreMsg: bool) = if msg in fatalMsgs: if conf.cmd == cmdIdeTools: log(s) quit(conf, msg) if msg >= errMin and msg <= errMax or - (msg in warnMin..hintMax and msg in conf.warningAsErrors): + (msg in warnMin..hintMax and msg in conf.warningAsErrors and not ignoreMsg): inc(conf.errorCounter) conf.exitcode = 1'i8 if conf.errorCounter >= conf.errorMax: @@ -524,8 +532,7 @@ proc liMessage*(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg: string, of warnMin..warnMax: sev = Severity.Warning ignoreMsg = not conf.hasWarn(msg) - if msg in conf.warningAsErrors: - ignoreMsg = false + if not ignoreMsg and msg in conf.warningAsErrors: title = ErrorTitle else: title = WarningTitle @@ -535,8 +542,7 @@ proc liMessage*(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg: string, of hintMin..hintMax: sev = Severity.Hint ignoreMsg = not conf.hasHint(msg) - if msg in conf.warningAsErrors: - ignoreMsg = false + if not ignoreMsg and msg in conf.warningAsErrors: title = ErrorTitle else: title = HintTitle @@ -562,7 +568,7 @@ proc liMessage*(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg: string, " compiler msg initiated here", KindColor, KindFormat % $hintMsgOrigin, resetStyle, conf.unitSep) - handleError(conf, msg, eh, s) + handleError(conf, msg, eh, s, ignoreMsg) if msg in fatalMsgs: # most likely would have died here but just in case, we restore state conf.m.errorOutputs = errorOutputsOld @@ -622,9 +628,9 @@ template internalAssert*(conf: ConfigRef, e: bool) = let arg = info2.toFileLineCol internalErrorImpl(conf, unknownLineInfo, arg, info2) -template lintReport*(conf: ConfigRef; info: TLineInfo, beau, got: string, forceHint = false, extraMsg = "") = +template lintReport*(conf: ConfigRef; info: TLineInfo, beau, got: string, extraMsg = "") = let m = "'$1' should be: '$2'$3" % [got, beau, extraMsg] - let msg = if optStyleError in conf.globalOptions and not forceHint: errGenerated else: hintName + let msg = if optStyleError in conf.globalOptions: errGenerated else: hintName liMessage(conf, info, msg, m, doNothing, instLoc()) proc quotedFilename*(conf: ConfigRef; i: TLineInfo): Rope = @@ -677,7 +683,7 @@ proc genSuccessX*(conf: ConfigRef) = const debugModeHints = "none (DEBUG BUILD, `-d:release` generates faster code)" if conf.cmd in cmdBackends: if conf.backend != backendJs: - build.add "gc: $#; " % $conf.selectedGC + build.add "mm: $#; " % $conf.selectedGC if optThreads in conf.globalOptions: build.add "threads: on; " build.add "opt: " if optOptimizeSpeed in conf.options: build.add "speed" diff --git a/compiler/nim.cfg b/compiler/nim.cfg index 4a0287cb5b16..5e8471f7052a 100644 --- a/compiler/nim.cfg +++ b/compiler/nim.cfg @@ -5,11 +5,14 @@ hint[XDeclaredButNotUsed]:off define:booting define:nimcore define:nimPreviewFloatRoundtrip +define:nimPreviewSlimSystem +gc:refc #import:"$projectpath/testability" @if windows: cincludes: "$lib/wrappers/libffi/common" + tlsEmulation:off @end define:useStdoutAsStdmsg diff --git a/compiler/nim.nim b/compiler/nim.nim index bfb07ba20ae8..2a6b3bc1b704 100644 --- a/compiler/nim.nim +++ b/compiler/nim.nim @@ -92,9 +92,15 @@ proc handleCmdLine(cache: IdentCache; conf: ConfigRef) = return self.processCmdLineAndProjectPath(conf) + var graph = newModuleGraph(cache, conf) if not self.loadConfigsAndProcessCmdLine(cache, conf, graph): return + + if conf.selectedGC == gcUnselected: + if conf.backend in {backendC, backendCpp, backendObjc}: + initOrcDefines(conf) + mainCommand(graph) if conf.hasHint(hintGCStats): echo(GC_getStatistics()) #echo(GC_getStatistics()) @@ -122,7 +128,7 @@ proc handleCmdLine(cache: IdentCache; conf: ConfigRef) = # `The parameter is incorrect` let cmd = cmdPrefix & output.quoteShell & ' ' & conf.arguments execExternalProgram(conf, cmd.strip(leading=false,trailing=true)) - of cmdDocLike, cmdRst2html, cmdRst2tex: # bugfix(cmdRst2tex was missing) + of cmdDocLike, cmdRst2html, cmdRst2tex, cmdMd2html, cmdMd2tex: # bugfix(cmdRst2tex was missing) if conf.arguments.len > 0: # reserved for future use rawMessage(conf, errGenerated, "'$1 cannot handle arguments" % [$conf.cmd]) diff --git a/compiler/nimfix/prettybase.nim b/compiler/nimfix/prettybase.nim index fbc2e3bd1857..78c24bae30cb 100644 --- a/compiler/nimfix/prettybase.nim +++ b/compiler/nimfix/prettybase.nim @@ -22,7 +22,10 @@ proc replaceDeprecated*(conf: ConfigRef; info: TLineInfo; oldSym, newSym: PIdent let last = first+identLen(line, first)-1 if cmpIgnoreStyle(line[first..last], oldSym.s) == 0: var x = line.substr(0, first-1) & newSym.s & line.substr(last+1) - system.shallowCopy(conf.m.fileInfos[info.fileIndex.int32].lines[info.line.int-1], x) + when defined(gcArc) or defined(gcOrc): + conf.m.fileInfos[info.fileIndex.int32].lines[info.line.int-1] = move x + else: + system.shallowCopy(conf.m.fileInfos[info.fileIndex.int32].lines[info.line.int-1], x) conf.m.fileInfos[info.fileIndex.int32].dirty = true #if newSym.s == "File": writeStackTrace() @@ -35,5 +38,8 @@ proc replaceComment*(conf: ConfigRef; info: TLineInfo) = if line[first] != '#': inc first var x = line.substr(0, first-1) & "discard " & line.substr(first+1).escape - system.shallowCopy(conf.m.fileInfos[info.fileIndex.int32].lines[info.line.int-1], x) + when defined(gcArc) or defined(gcOrc): + conf.m.fileInfos[info.fileIndex.int32].lines[info.line.int-1] = move x + else: + system.shallowCopy(conf.m.fileInfos[info.fileIndex.int32].lines[info.line.int-1], x) conf.m.fileInfos[info.fileIndex.int32].dirty = true diff --git a/compiler/nimpaths.nim b/compiler/nimpaths.nim index c6e188289224..3756f956b57c 100644 --- a/compiler/nimpaths.nim +++ b/compiler/nimpaths.nim @@ -9,7 +9,7 @@ specialpaths is simpler because it doesn't need variables to be relocatable at runtime (eg for use in testament) interpolation variables: - $nimr: such that `$nimr/lib/system.nim` exists (avoids confusion with $nim binary) +: $nimr: such that `$nimr/lib/system.nim` exists (avoids confusion with $nim binary) in compiler, it's obtainable via getPrefixDir(); for other tools (eg koch), this could be getCurrentDir() or getAppFilename().parentDir.parentDir, depending on use case diff --git a/compiler/optimizer.nim b/compiler/optimizer.nim index f484fdbf5c01..10b092e112cc 100644 --- a/compiler/optimizer.nim +++ b/compiler/optimizer.nim @@ -178,7 +178,7 @@ proc analyse(c: var Con; b: var BasicBlock; n: PNode) = of nkCaseStmt: let isExhaustive = skipTypes(n[0].typ, - abstractVarRange-{tyTypeDesc}).kind notin {tyFloat..tyFloat128, tyString} or + abstractVarRange-{tyTypeDesc}).kind notin {tyFloat..tyFloat128, tyString, tyCstring} or n[^1].kind == nkElse analyse(c, b, n[0]) diff --git a/compiler/options.nim b/compiler/options.nim index a019916ec7f8..8df4c7bead95 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -153,6 +153,8 @@ type cmdDoc2tex # convert .nim doc comments to LaTeX cmdRst2html # convert a reStructuredText file to HTML cmdRst2tex # convert a reStructuredText file to TeX + cmdMd2html # convert a Markdown file to HTML + cmdMd2tex # convert a Markdown file to TeX cmdJsondoc0 cmdJsondoc cmdCtags @@ -183,14 +185,14 @@ type gcMarkAndSweep = "markAndSweep" gcHooks = "hooks" gcRefc = "refc" - gcV2 = "v2" gcGo = "go" # gcRefc and the GCs that follow it use a write barrier, # as far as usesWriteBarrier() is concerned IdeCmd* = enum - ideNone, ideSug, ideCon, ideDef, ideUse, ideDus, ideChk, ideMod, - ideHighlight, ideOutline, ideKnown, ideMsg, ideProject + ideNone, ideSug, ideCon, ideDef, ideUse, ideDus, ideChk, ideChkFile, ideMod, + ideHighlight, ideOutline, ideKnown, ideMsg, ideProject, ideGlobalSymbols, + ideRecompile, ideChanged, ideType Feature* = enum ## experimental features; DO NOT RENAME THESE! implicitDeref, @@ -211,9 +213,9 @@ type strictFuncs, views, strictNotNil, - overloadableEnums, + overloadableEnums, # deadcode strictEffects, - unicodeOperators, + unicodeOperators, # deadcode flexibleOptionalParams LegacyFeature* = enum @@ -502,7 +504,7 @@ when defined(nimDebugUtils): export debugutils proc initConfigRefCommon(conf: ConfigRef) = - conf.selectedGC = gcRefc + conf.selectedGC = gcUnselected conf.verbosity = 1 conf.hintProcessingDots = true conf.options = DefaultOptions @@ -807,6 +809,8 @@ proc toGeneratedFile*(conf: ConfigRef; path: AbsoluteFile, proc completeGeneratedFilePath*(conf: ConfigRef; f: AbsoluteFile, createSubDir: bool = true): AbsoluteFile = + ## Return an absolute path of a generated intermediary file. + ## Optionally creates the cache directory if `createSubDir` is `true`. let subdir = getNimcacheDir(conf) if createSubDir: try: @@ -814,11 +818,6 @@ proc completeGeneratedFilePath*(conf: ConfigRef; f: AbsoluteFile, except OSError: conf.quitOrRaise "cannot create directory: " & subdir.string result = subdir / RelativeFile f.string.splitPath.tail - #echo "completeGeneratedFilePath(", f, ") = ", result - -proc toRodFile*(conf: ConfigRef; f: AbsoluteFile; ext = RodExt): AbsoluteFile = - result = changeFileExt(completeGeneratedFilePath(conf, - withPackageName(conf, f)), ext) proc rawFindFile(conf: ConfigRef; f: RelativeFile; suppressStdlib: bool): AbsoluteFile = for it in conf.searchPaths: @@ -855,7 +854,7 @@ when (NimMajor, NimMinor) < (1, 1) or not declared(isRelativeTo): let ret = relativePath(path, base) result = path.len > 0 and not ret.startsWith ".." -const stdlibDirs = [ +const stdlibDirs* = [ "pure", "core", "arch", "pure/collections", "pure/concurrency", @@ -997,12 +996,17 @@ proc parseIdeCmd*(s: string): IdeCmd = of "use": ideUse of "dus": ideDus of "chk": ideChk + of "chkFile": ideChkFile of "mod": ideMod of "highlight": ideHighlight of "outline": ideOutline of "known": ideKnown of "msg": ideMsg of "project": ideProject + of "globalSymbols": ideGlobalSymbols + of "recompile": ideRecompile + of "changed": ideChanged + of "type": ideType else: ideNone proc `$`*(c: IdeCmd): string = @@ -1013,6 +1017,7 @@ proc `$`*(c: IdeCmd): string = of ideUse: "use" of ideDus: "dus" of ideChk: "chk" + of ideChkFile: "chkFile" of ideMod: "mod" of ideNone: "none" of ideHighlight: "highlight" @@ -1020,6 +1025,10 @@ proc `$`*(c: IdeCmd): string = of ideKnown: "known" of ideMsg: "msg" of ideProject: "project" + of ideGlobalSymbols: "globalSymbols" + of ideRecompile: "recompile" + of ideChanged: "changed" + of ideType: "type" proc floatInt64Align*(conf: ConfigRef): int16 = ## Returns either 4 or 8 depending on reasons. diff --git a/compiler/packagehandling.nim b/compiler/packagehandling.nim index 4af0c28fa9aa..8cf209779eb1 100644 --- a/compiler/packagehandling.nim +++ b/compiler/packagehandling.nim @@ -37,24 +37,7 @@ proc getNimbleFile*(conf: ConfigRef; path: string): string = proc getPackageName*(conf: ConfigRef; path: string): string = ## returns nimble package name, e.g.: `cligen` let path = getNimbleFile(conf, path) - result = path.splitFile.name - -proc fakePackageName*(conf: ConfigRef; path: AbsoluteFile): string = - # Convert `path` so that 2 modules with same name - # in different directory get different name and they can be - # placed in a directory. - # foo-#head/../bar becomes @foo-@hhead@s..@sbar - result = "@m" & relativeTo(path, conf.projectPath).string.multiReplace( - {$os.DirSep: "@s", $os.AltSep: "@s", "#": "@h", "@": "@@", ":": "@c"}) - -proc demanglePackageName*(path: string): string = - result = path.multiReplace({"@@": "@", "@h": "#", "@s": "/", "@m": "", "@c": ":"}) - -proc withPackageName*(conf: ConfigRef; path: AbsoluteFile): AbsoluteFile = - let x = getPackageName(conf, path.string) - let (p, file, ext) = path.splitFile - if x == "stdlib": - # Hot code reloading now relies on 'stdlib_system' names etc. - result = p / RelativeFile((x & '_' & file) & ext) + if path.len > 0: + return path.splitFile.name else: - result = p / RelativeFile(fakePackageName(conf, path)) + return "unknown" diff --git a/compiler/packages.nim b/compiler/packages.nim new file mode 100644 index 000000000000..d8b97e374ce5 --- /dev/null +++ b/compiler/packages.nim @@ -0,0 +1,53 @@ +# +# +# The Nim Compiler +# (c) Copyright 2022 Andreas Rumpf +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +## Package related procs. +## +## See Also: +## * `packagehandling` for package path handling +## * `modulegraphs.getPackage` +## * `modulegraphs.belongsToStdlib` + +import "." / [options, ast, lineinfos, idents, pathutils, msgs] + +when defined(nimPreviewSlimSystem): + import std/assertions + + +proc getPackage*(conf: ConfigRef; cache: IdentCache; fileIdx: FileIndex): PSym = + ## Return a new package symbol. + ## + ## See Also: + ## * `modulegraphs.getPackage` + let + filename = AbsoluteFile toFullPath(conf, fileIdx) + name = getIdent(cache, splitFile(filename).name) + info = newLineInfo(fileIdx, 1, 1) + pkgName = getPackageName(conf, filename.string) + pkgIdent = getIdent(cache, pkgName) + newSym(skPackage, pkgIdent, ItemId(module: PackageModuleId, item: int32(fileIdx)), nil, info) + +func getPackageSymbol*(sym: PSym): PSym = + ## Return the owning package symbol. + assert sym != nil + result = sym + while result.kind != skPackage: + result = result.owner + assert result != nil, repr(sym.info) + +func getPackageId*(sym: PSym): int = + ## Return the owning package ID. + sym.getPackageSymbol.id + +func belongsToProjectPackage*(conf: ConfigRef, sym: PSym): bool = + ## Return whether the symbol belongs to the project's package. + ## + ## See Also: + ## * `modulegraphs.belongsToStdlib` + conf.mainPackageId == sym.getPackageId diff --git a/compiler/parampatterns.nim b/compiler/parampatterns.nim index cceb236ae095..38b7faa0747c 100644 --- a/compiler/parampatterns.nim +++ b/compiler/parampatterns.nim @@ -224,7 +224,7 @@ proc isAssignable*(owner: PSym, n: PNode): TAssignableResult = const kinds = {skVar, skResult, skTemp, skParam, skLet, skForVar} if n.sym.kind == skParam: result = if n.sym.typ.kind in {tyVar, tySink}: arLValue else: arAddressableConst - elif n.sym.kind == skConst and dontInlineConstant(n, n.sym.ast): + elif n.sym.kind == skConst and dontInlineConstant(n, n.sym.astdef): result = arAddressableConst elif n.sym.kind in kinds: if n.sym.kind in {skParam, skLet, skForVar}: diff --git a/compiler/parser.nim b/compiler/parser.nim index 52466d9fa0f0..8b0a9d1c6450 100644 --- a/compiler/parser.nim +++ b/compiler/parser.nim @@ -20,6 +20,8 @@ when isMainModule: # Leave a note in grammar.txt that it is generated: #| # This file is generated by compiler/parser.nim. import pegs + when defined(nimPreviewSlimSystem): + import std/syncio var outp = open("doc/grammar.txt", fmWrite) for line in lines("compiler/parser.nim"): if line =~ peg" \s* '#| ' {.*}": @@ -2031,15 +2033,10 @@ proc parseObjectPart(p: var Parser): PNode = result = p.emptyNode proc parseObject(p: var Parser): PNode = - #| objectDecl = 'object' pragma? ('of' typeDesc)? COMMENT? objectPart + #| objectDecl = 'object' ('of' typeDesc)? COMMENT? objectPart result = newNodeP(nkObjectTy, p) getTok(p) - if p.tok.tokType == tkCurlyDotLe and p.validInd: - # Deprecated since v0.20.0 - parMessage(p, warnDeprecated, "type pragmas follow the type name; this form of writing pragmas is deprecated") - result.add(parsePragma(p)) - else: - result.add(p.emptyNode) + result.add(p.emptyNode) # compatibility with old pragma node if p.tok.tokType == tkOf and p.tok.indent < 0: var a = newNodeP(nkOfInherit, p) getTok(p) @@ -2117,39 +2114,24 @@ proc parseTypeClass(p: var Parser): PNode = proc parseTypeDef(p: var Parser): PNode = #| - #| typeDef = identWithPragmaDot genericParamList? '=' optInd typeDefAux - #| indAndComment? / identVisDot genericParamList? pragma '=' optInd typeDefAux + #| typeDef = identVisDot genericParamList? pragma '=' optInd typeDefAux #| indAndComment? result = newNodeP(nkTypeDef, p) var identifier = identVis(p, allowDot=true) var identPragma = identifier var pragma: PNode var genericParam: PNode - var noPragmaYet = true - - if p.tok.tokType == tkCurlyDotLe: - pragma = optPragmas(p) - identPragma = newNodeP(nkPragmaExpr, p) - identPragma.add(identifier) - identPragma.add(pragma) - noPragmaYet = false if p.tok.tokType == tkBracketLe and p.validInd: - if not noPragmaYet: - # Deprecated since v0.20.0 - parMessage(p, warnDeprecated, "pragma before generic parameter list is deprecated") genericParam = parseGenericParamList(p) else: genericParam = p.emptyNode - if noPragmaYet: - pragma = optPragmas(p) - if pragma.kind != nkEmpty: - identPragma = newNodeP(nkPragmaExpr, p) - identPragma.add(identifier) - identPragma.add(pragma) - elif p.tok.tokType == tkCurlyDotLe: - parMessage(p, errGenerated, "pragma already present") + pragma = optPragmas(p) + if pragma.kind != nkEmpty: + identPragma = newNodeP(nkPragmaExpr, p) + identPragma.add(identifier) + identPragma.add(pragma) result.add(identPragma) result.add(genericParam) diff --git a/compiler/passaux.nim b/compiler/passaux.nim index 68b7832489bb..af507d210876 100644 --- a/compiler/passaux.nim +++ b/compiler/passaux.nim @@ -22,6 +22,8 @@ proc verboseOpen(graph: ModuleGraph; s: PSym; idgen: IdGenerator): PPassContext # xxx consider either removing this or keeping for documentation for how to add a pass result = VerboseRef(config: graph.config, idgen: idgen) +import std/objectdollar + proc verboseProcess(context: PPassContext, n: PNode): PNode = # called from `process` in `processTopLevelStmt`. result = n diff --git a/compiler/passes.nim b/compiler/passes.nim index 7fb2842f50c8..46c36f9d127d 100644 --- a/compiler/passes.nim +++ b/compiler/passes.nim @@ -14,7 +14,7 @@ import options, ast, llstream, msgs, idents, syntaxes, modulegraphs, reorder, - lineinfos, pathutils + lineinfos, pathutils, std/sha1, packages when defined(nimPreviewSlimSystem): import std/syncio @@ -104,7 +104,7 @@ const proc prepareConfigNotes(graph: ModuleGraph; module: PSym) = # don't be verbose unless the module belongs to the main package: - if module.getnimblePkgId == graph.config.mainPackageId: + if graph.config.belongsToProjectPackage(module): graph.config.notes = graph.config.mainPackageNotes else: if graph.config.mainPackageNotes == {}: graph.config.mainPackageNotes = graph.config.notes @@ -114,12 +114,6 @@ proc moduleHasChanged*(graph: ModuleGraph; module: PSym): bool {.inline.} = result = true #module.id >= 0 or isDefined(graph.config, "nimBackendAssumesChange") -proc partOfStdlib(x: PSym): bool = - var it = x.owner - while it != nil and it.kind == skPackage and it.owner != nil: - it = it.owner - result = it != nil and it.name.s == "stdlib" - proc processModule*(graph: ModuleGraph; module: PSym; idgen: IdGenerator; stream: PLLStream): bool {.discardable.} = if graph.stopCompile(): return true @@ -138,10 +132,15 @@ proc processModule*(graph: ModuleGraph; module: PSym; idgen: IdGenerator; return false else: s = stream + + when defined(nimsuggest): + let filename = toFullPathConsiderDirty(graph.config, fileIdx).string + msgs.setHash(graph.config, fileIdx, $sha1.secureHashFile(filename)) + while true: openParser(p, fileIdx, s, graph.cache, graph.config) - if not partOfStdlib(module) or module.name.s == "distros": + if not belongsToStdlib(graph, module) or (belongsToStdlib(graph, module) and module.name.s == "distros"): # XXX what about caching? no processing then? what if I change the # modules to include between compilation runs? we'd need to track that # in ROD files. I think we should enable this feature only diff --git a/compiler/patterns.nim b/compiler/patterns.nim index 87e9c825cd1a..d4577981f037 100644 --- a/compiler/patterns.nim +++ b/compiler/patterns.nim @@ -146,7 +146,7 @@ proc matches(c: PPatternContext, p, n: PNode): bool = elif n.kind == nkSym and n.sym.kind == skConst: # try both: if p.kind == nkSym: result = p.sym == n.sym - elif matches(c, p, n.sym.ast): result = true + elif matches(c, p, n.sym.astdef): result = true elif p.kind == nkPattern: # pattern operators: | * let opr = p[0].ident.s diff --git a/compiler/plugins/locals.nim b/compiler/plugins/locals.nim index c03a6986e443..384780f7ba2e 100644 --- a/compiler/plugins/locals.nim +++ b/compiler/plugins/locals.nim @@ -15,7 +15,7 @@ import ".." / [ast, astalgo, proc semLocals*(c: PContext, n: PNode): PNode = var counter = 0 var tupleType = newTypeS(tyTuple, c) - result = newNodeIT(nkPar, n.info, tupleType) + result = newNodeIT(nkTupleConstr, n.info, tupleType) tupleType.n = newNodeI(nkRecList, n.info) let owner = getCurrOwner(c) # for now we skip openarrays ... diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim index 458c7547a5a2..a312478aef8f 100644 --- a/compiler/pragmas.nim +++ b/compiler/pragmas.nim @@ -32,20 +32,20 @@ const wCompilerProc, wNonReloadable, wCore, wProcVar, wVarargs, wCompileTime, wMerge, wBorrow, wImportCompilerProc, wThread, wAsmNoStackFrame, wDiscardable, wNoInit, wCodegenDecl, - wGensym, wInject, wRaises, wEffectsOf, wTags, wLocks, wDelegator, wGcSafe, + wGensym, wInject, wRaises, wEffectsOf, wTags, wForbids, wLocks, wDelegator, wGcSafe, wConstructor, wLiftLocals, wStackTrace, wLineTrace, wNoDestroy, wRequires, wEnsures, wEnforceNoRaises} converterPragmas* = procPragmas methodPragmas* = procPragmas+{wBase}-{wImportCpp} templatePragmas* = {wDeprecated, wError, wGensym, wInject, wDirty, - wDelegator, wExportNims, wUsed, wPragma} + wDelegator, wExportNims, wUsed, wPragma, wRedefine} macroPragmas* = declPragmas + {FirstCallConv..LastCallConv, wMagic, wNoSideEffect, wCompilerProc, wNonReloadable, wCore, wDiscardable, wGensym, wInject, wDelegator} iteratorPragmas* = declPragmas + {FirstCallConv..LastCallConv, wNoSideEffect, wSideEffect, wMagic, wBorrow, wDiscardable, wGensym, wInject, wRaises, wEffectsOf, - wTags, wLocks, wGcSafe, wRequires, wEnsures} + wTags, wForbids, wLocks, wGcSafe, wRequires, wEnsures} exprPragmas* = {wLine, wLocks, wNoRewrite, wGcSafe, wNoSideEffect} stmtPragmas* = { wHint, wWarning, wError, @@ -55,7 +55,7 @@ const wDeprecated, wPragma, wEmit, wUnroll, wLinearScanEnd, wPatterns, wTrMacros, wEffects, wNoForward, wReorder, wComputedGoto, - wExperimental, wThis, wUsed, wInvariant, wAssume, wAssert} + wExperimental, wDoctype, wThis, wUsed, wInvariant, wAssume, wAssert} stmtPragmasTopLevel* = {wChecks, wObjChecks, wFieldChecks, wRangeChecks, wBoundChecks, wOverflowChecks, wNilChecks, wStaticBoundchecks, wStyleChecks, wAssertions, @@ -65,7 +65,7 @@ const lambdaPragmas* = {FirstCallConv..LastCallConv, wNoSideEffect, wSideEffect, wNoreturn, wNosinks, wDynlib, wHeader, wThread, wAsmNoStackFrame, - wRaises, wLocks, wTags, wRequires, wEnsures, wEffectsOf, + wRaises, wLocks, wTags, wForbids, wRequires, wEnsures, wEffectsOf, wGcSafe, wCodegenDecl, wNoInit, wCompileTime} typePragmas* = declPragmas + {wMagic, wAcyclic, wPure, wHeader, wCompilerProc, wCore, wFinal, wSize, wShallow, @@ -85,7 +85,7 @@ const paramPragmas* = {wNoalias, wInject, wGensym} letPragmas* = varPragmas procTypePragmas* = {FirstCallConv..LastCallConv, wVarargs, wNoSideEffect, - wThread, wRaises, wEffectsOf, wLocks, wTags, wGcSafe, + wThread, wRaises, wEffectsOf, wLocks, wTags, wForbids, wGcSafe, wRequires, wEnsures} forVarPragmas* = {wInject, wGensym} allRoutinePragmas* = methodPragmas + iteratorPragmas + lambdaPragmas @@ -313,7 +313,7 @@ proc expectDynlibNode(c: PContext, n: PNode): PNode = # {.dynlib: myGetProcAddr(...).} result = c.semExpr(c, n[1]) if result.kind == nkSym and result.sym.kind == skConst: - result = result.sym.ast # look it up + result = result.sym.astdef # look it up if result.typ == nil or result.typ.kind notin {tyPointer, tyString, tyProc}: localError(c.config, n.info, errStringLiteralExpected) result = newEmptyStrNode(c, n) @@ -511,7 +511,10 @@ proc processCompile(c: PContext, n: PNode) = n[i] = c.semConstExpr(c, n[i]) case n[i].kind of nkStrLit, nkRStrLit, nkTripleStrLit: - shallowCopy(result, n[i].strVal) + when defined(gcArc) or defined(gcOrc): + result = n[i].strVal + else: + shallowCopy(result, n[i].strVal) else: localError(c.config, n.info, errStringLiteralExpected) result = "" @@ -641,12 +644,13 @@ proc pragmaLine(c: PContext, n: PNode) = n.info = getInfoContext(c.config, -1) proc processPragma(c: PContext, n: PNode, i: int) = + ## Create and add a new custom pragma `{.pragma: name.}` node to the module's context. let it = n[i] if it.kind notin nkPragmaCallKinds and it.safeLen == 2: invalidPragma(c, n) elif it.safeLen != 2 or it[0].kind != nkIdent or it[1].kind != nkIdent: invalidPragma(c, n) - var userPragma = newSym(skTemplate, it[1].ident, nextSymId(c.idgen), nil, it.info, c.config.options) + var userPragma = newSym(skTemplate, it[1].ident, nextSymId(c.idgen), c.module, it.info, c.config.options) userPragma.ast = newTreeI(nkPragma, n.info, n.sons[i+1..^1]) strTableAdd(c.userPragmas, userPragma) @@ -819,7 +823,7 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int, elif not isStatement: localError(c.config, n.info, "'cast' pragma only allowed in a statement context") case whichPragma(key[1]) - of wRaises, wTags: pragmaRaisesOrTags(c, key[1]) + of wRaises, wTags, wForbids: pragmaRaisesOrTags(c, key[1]) else: discard return elif key.kind notin nkIdentKinds: @@ -828,8 +832,7 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int, let ident = considerQuotedIdent(c, key) var userPragma = strTableGet(c.userPragmas, ident) if userPragma != nil: - if {optStyleHint, optStyleError} * c.config.globalOptions != {}: - styleCheckUse(c.config, key.info, userPragma) + styleCheckUse(c, key.info, userPragma) # number of pragmas increase/decrease with user pragma expansion inc c.instCounter @@ -843,8 +846,7 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int, else: let k = whichKeyword(ident) if k in validPragmas: - if {optStyleHint, optStyleError} * c.config.globalOptions != {}: - checkPragmaUse(c.config, key.info, k, ident.s) + checkPragmaUse(c.config, key.info, k, ident.s) case k of wExportc, wExportCpp: makeExternExport(c, sym, getOptionalStr(c, it, "$1"), it.info) @@ -868,6 +870,9 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int, of wDirty: if sym.kind == skTemplate: incl(sym.flags, sfDirty) else: invalidPragma(c, it) + of wRedefine: + if sym.kind == skTemplate: incl(sym.flags, sfTemplateRedefinition) + else: invalidPragma(c, it) of wImportCpp: processImportCpp(c, sym, getOptionalStr(c, it, "$1"), it.info) of wCppNonPod: @@ -955,7 +960,7 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int, incl(sym.loc.flags, lfHeader) incl(sym.loc.flags, lfNoDecl) # implies nodecl, because otherwise header would not make sense - if sym.loc.r == nil: sym.loc.r = rope(sym.name.s) + if sym.loc.r == "": sym.loc.r = rope(sym.name.s) of wNoSideEffect: noVal(c, it) if sym != nil: @@ -988,12 +993,12 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int, of wNonReloadable: sym.flags.incl sfNonReloadable of wProcVar: + # old procvar annotation, no longer needed noVal(c, it) - incl(sym.flags, sfProcvar) of wExplain: sym.flags.incl sfExplain of wDeprecated: - if sym != nil and sym.kind in routineKinds + {skType, skVar, skLet}: + if sym != nil and sym.kind in routineKinds + {skType, skVar, skLet, skConst}: if it.kind in nkPragmaCallKinds: discard getStrLitNode(c, it) incl(sym.flags, sfDeprecated) elif sym != nil and sym.kind != skModule: @@ -1038,7 +1043,6 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int, of wThread: noVal(c, it) incl(sym.flags, sfThread) - incl(sym.flags, sfProcvar) if sym.typ != nil: incl(sym.typ.flags, tfThread) if sym.typ.callConv == ccClosure: sym.typ.callConv = ccNimCall @@ -1185,7 +1189,7 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int, noVal(c, it) if sym == nil: invalidPragma(c, it) of wLine: pragmaLine(c, it) - of wRaises, wTags: pragmaRaisesOrTags(c, it) + of wRaises, wTags, wForbids: pragmaRaisesOrTags(c, it) of wLocks: if sym == nil: pragmaLockStmt(c, it) elif sym.typ == nil: invalidPragma(c, it) @@ -1214,15 +1218,9 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int, if not isTopLevel(c): localError(c.config, n.info, "'experimental' pragma only valid as toplevel statement or in a 'push' environment") processExperimental(c, it) - of wThis: - if it.kind in nkPragmaCallKinds and it.len == 2: - c.selfName = considerQuotedIdent(c, it[1]) - message(c.config, n.info, warnDeprecated, "'.this' pragma is deprecated") - elif it.kind == nkIdent or it.len == 1: - c.selfName = getIdent(c.cache, "self") - message(c.config, n.info, warnDeprecated, "'.this' pragma is deprecated") - else: - localError(c.config, it.info, "'this' pragma is allowed to have zero or one arguments") + of wDoctype: + if not isTopLevel(c): + localError(c.config, n.info, "\"doctype\" pragma only valid as top-level statement") of wNoRewrite: noVal(c, it) of wBase: @@ -1249,7 +1247,7 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int, elif comesFromPush and whichKeyword(ident) != wInvalid: discard "ignore the .push pragma; it doesn't apply" else: - if sym == nil or (sym.kind in {skVar, skLet, skParam, + if sym == nil or (sym.kind in {skVar, skLet, skConst, skParam, skIterator, skField, skProc, skFunc, skConverter, skMethod, skType}): n[i] = semCustomPragma(c, it) elif sym != nil: @@ -1292,7 +1290,7 @@ proc implicitPragmas*(c: PContext, sym: PSym, info: TLineInfo, sfImportc in sym.flags and lib != nil: incl(sym.loc.flags, lfDynamicLib) addToLib(lib, sym) - if sym.loc.r == nil: sym.loc.r = rope(sym.name.s) + if sym.loc.r == "": sym.loc.r = rope(sym.name.s) proc hasPragma*(n: PNode, pragma: TSpecialWord): bool = if n == nil: return false diff --git a/compiler/renderer.nim b/compiler/renderer.nim index f847aa0944eb..1412405189e9 100644 --- a/compiler/renderer.nim +++ b/compiler/renderer.nim @@ -18,7 +18,7 @@ import lexer, options, idents, strutils, ast, msgs, lineinfos when defined(nimPreviewSlimSystem): - import std/[syncio, assertions] + import std/[syncio, assertions, formatfloat] type TRenderFlag* = enum @@ -398,18 +398,18 @@ proc atom(g: TSrcGen; n: PNode): string = of nkUInt64Lit: result = ulitAux(g, n, n.intVal, 8) & "\'u64" of nkFloatLit: if n.flags * {nfBase2, nfBase8, nfBase16} == {}: result = $(n.floatVal) - else: result = litAux(g, n, (cast[PInt64](addr(n.floatVal)))[] , 8) + else: result = litAux(g, n, (cast[ptr int64](addr(n.floatVal)))[] , 8) of nkFloat32Lit: if n.flags * {nfBase2, nfBase8, nfBase16} == {}: result = $n.floatVal & "\'f32" else: f = n.floatVal.float32 - result = litAux(g, n, (cast[PInt32](addr(f)))[], 4) & "\'f32" + result = litAux(g, n, (cast[ptr int32](addr(f)))[], 4) & "\'f32" of nkFloat64Lit: if n.flags * {nfBase2, nfBase8, nfBase16} == {}: result = $n.floatVal & "\'f64" else: - result = litAux(g, n, (cast[PInt64](addr(n.floatVal)))[], 8) & "\'f64" + result = litAux(g, n, (cast[ptr int64](addr(n.floatVal)))[], 8) & "\'f64" of nkNilLit: result = "nil" of nkType: if (n.typ != nil) and (n.typ.sym != nil): result = n.typ.sym.name.s diff --git a/compiler/renderverbatim.nim b/compiler/renderverbatim.nim index fc74f2322c85..792079b3f44e 100644 --- a/compiler/renderverbatim.nim +++ b/compiler/renderverbatim.nim @@ -2,6 +2,10 @@ import strutils import ast, options, msgs +when defined(nimPreviewSlimSystem): + import std/assertions + + const isDebug = false when isDebug: import renderer diff --git a/compiler/rodutils.nim b/compiler/rodutils.nim index 95b7b2d9e3bd..e2e9e1eb2762 100644 --- a/compiler/rodutils.nim +++ b/compiler/rodutils.nim @@ -42,7 +42,7 @@ when not declared(signbit): proc signbit*(x: SomeFloat): bool {.inline.} = result = c_signbit(x) != 0 -import system/formatfloat +import std/formatfloat proc toStrMaxPrecision*(f: BiggestFloat | float32): string = const literalPostfix = when f is float32: "f" else: "" diff --git a/compiler/ropes.nim b/compiler/ropes.nim index a44d84ddcfe3..12eac733e7d9 100644 --- a/compiler/ropes.nim +++ b/compiler/ropes.nim @@ -7,217 +7,57 @@ # distribution, for details about the copyright. # -# Ropes for the C code generator -# -# Ropes are a data structure that represents a very long string -# efficiently; especially concatenation is done in O(1) instead of O(N). -# Ropes make use a lazy evaluation: They are essentially concatenation -# trees that are only flattened when converting to a native Nim -# string or when written to disk. The empty string is represented by a -# nil pointer. -# A little picture makes everything clear: -# -# "this string" & " is internally " & "represented as" -# -# con -- inner nodes do not contain raw data -# / \ -# / \ -# / \ -# con "represented as" -# / \ -# / \ -# / \ -# / \ -# / \ -#"this string" " is internally " -# -# Note that this is the same as: -# "this string" & (" is internally " & "represented as") -# -# con -# / \ -# / \ -# / \ -# "this string" con -# / \ -# / \ -# / \ -# / \ -# / \ -#" is internally " "represented as" -# -# The 'con' operator is associative! This does not matter however for -# the algorithms we use for ropes. -# -# Note that the left and right pointers are not needed for leaves. -# Leaves have relatively high memory overhead (~30 bytes on a 32 -# bit machines) and we produce many of them. This is why we cache and -# share leaves across different rope trees. -# To cache them they are inserted in a `cache` array. +# Ropes for the C code generator. Ropes are mapped to `string` directly nowadays. -import - hashes +import hashes from pathutils import AbsoluteFile when defined(nimPreviewSlimSystem): - import std/[assertions, syncio] - + import std/[assertions, syncio, formatfloat] type FormatStr* = string # later we may change it to CString for better # performance of the code generator (assignments # copy the format strings # though it is not necessary) - Rope* = ref RopeObj - RopeObj*{.acyclic.} = object of RootObj # the empty rope is represented - # by nil to safe space - left, right: Rope - L: int # <= 0 if a leaf - data*: string + Rope* = string -proc len*(a: Rope): int = - ## the rope's length - if a == nil: result = 0 - else: result = abs a.L +proc newRopeAppender*(): string {.inline.} = + result = newString(0) -proc newRope(data: string = ""): Rope = - new(result) - result.L = -data.len - result.data = data +proc freeze*(r: Rope) {.inline.} = discard -when not compileOption("threads"): - var - cache: array[0..2048*2 - 1, Rope] - - proc resetRopeCache* = - for i in low(cache)..high(cache): - cache[i] = nil +proc resetRopeCache* = discard -proc ropeInvariant(r: Rope): bool = - if r == nil: - result = true - else: - result = true # - # if r.data <> snil then - # result := true - # else begin - # result := (r.left <> nil) and (r.right <> nil); - # if result then result := ropeInvariant(r.left); - # if result then result := ropeInvariant(r.right); - # end - -var gCacheTries* = 0 -var gCacheMisses* = 0 -var gCacheIntTries* = 0 - -proc insertInCache(s: string): Rope = - when declared(cache): - inc gCacheTries - var h = hash(s) and high(cache) - result = cache[h] - if isNil(result) or result.data != s: - inc gCacheMisses - result = newRope(s) - cache[h] = result - else: - result = newRope(s) - -proc rope*(s: string): Rope = - ## Converts a string to a rope. - if s.len == 0: - result = nil - else: - result = insertInCache(s) - assert(ropeInvariant(result)) +template rope*(s: string): string = s proc rope*(i: BiggestInt): Rope = ## Converts an int to a rope. - inc gCacheIntTries result = rope($i) proc rope*(f: BiggestFloat): Rope = ## Converts a float to a rope. result = rope($f) -proc `&`*(a, b: Rope): Rope = - if a == nil: - result = b - elif b == nil: - result = a - else: - result = newRope() - result.L = abs(a.L) + abs(b.L) - result.left = a - result.right = b - -proc `&`*(a: Rope, b: string): Rope = - ## the concatenation operator for ropes. - result = a & rope(b) - -proc `&`*(a: string, b: Rope): Rope = - ## the concatenation operator for ropes. - result = rope(a) & b - -proc `&`*(a: openArray[Rope]): Rope = - ## the concatenation operator for an openarray of ropes. - for i in 0..high(a): result = result & a[i] - -proc add*(a: var Rope, b: Rope) = - ## adds `b` to the rope `a`. - a = a & b - -proc add*(a: var Rope, b: string) = - ## adds `b` to the rope `a`. - a = a & b - -iterator leaves*(r: Rope): string = - ## iterates over any leaf string in the rope `r`. - if r != nil: - var stack = @[r] - while stack.len > 0: - var it = stack.pop - while it.left != nil: - assert it.right != nil - stack.add(it.right) - it = it.left - assert(it != nil) - yield it.data - -iterator items*(r: Rope): char = - ## iterates over any character in the rope `r`. - for s in leaves(r): - for c in items(s): yield c - proc writeRope*(f: File, r: Rope) = ## writes a rope to a file. - for s in leaves(r): write(f, s) + write(f, r) proc writeRope*(head: Rope, filename: AbsoluteFile): bool = var f: File if open(f, filename.string, fmWrite): - if head != nil: writeRope(f, head) + writeRope(f, head) close(f) result = true else: result = false -proc `$`*(r: Rope): string = - ## converts a rope back to a string. - result = newString(r.len) - setLen(result, 0) - for s in leaves(r): result.add(s) - -proc ropeConcat*(a: varargs[Rope]): Rope = - # not overloaded version of concat to speed-up `rfmt` a little bit - for i in 0..high(a): result = result & a[i] - -proc prepend*(a: var Rope, b: Rope) = a = b & a proc prepend*(a: var Rope, b: string) = a = b & a proc runtimeFormat*(frmt: FormatStr, args: openArray[Rope]): Rope = var i = 0 - result = nil + result = newRopeAppender() var num = 0 while i < frmt.len: if frmt[i] == '$': @@ -270,7 +110,6 @@ proc runtimeFormat*(frmt: FormatStr, args: openArray[Rope]): Rope = else: break if i - 1 >= start: result.add(substr(frmt, start, i - 1)) - assert(ropeInvariant(result)) proc `%`*(frmt: static[FormatStr], args: openArray[Rope]): Rope = runtimeFormat(frmt, args) @@ -279,21 +118,10 @@ template addf*(c: var Rope, frmt: FormatStr, args: openArray[Rope]) = ## shortcut for ``add(c, frmt % args)``. c.add(frmt % args) -when true: - template `~`*(r: string): Rope = r % [] -else: - {.push stack_trace: off, line_trace: off.} - proc `~`*(r: static[string]): Rope = - # this is the new optimized "to rope" operator - # the mnemonic is that `~` looks a bit like a rope :) - var r {.global.} = r % [] - return r - {.pop.} - const bufSize = 1024 # 1 KB is reasonable -proc equalsFile*(r: Rope, f: File): bool = +proc equalsFile*(s: Rope, f: File): bool = ## returns true if the contents of the file `f` equal `r`. var buf: array[bufSize, char] @@ -302,7 +130,7 @@ proc equalsFile*(r: Rope, f: File): bool = btotal = 0 rtotal = 0 - for s in leaves(r): + when true: var spos = 0 rtotal += s.len while spos < s.len: diff --git a/compiler/sem.nim b/compiler/sem.nim index 70c57864c5d0..b6ee76cc9bf4 100644 --- a/compiler/sem.nim +++ b/compiler/sem.nim @@ -25,13 +25,16 @@ when defined(nimfix): when not defined(leanCompiler): import spawn +when defined(nimPreviewSlimSystem): + import std/formatfloat + # implementation -proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode -proc semExprWithType(c: PContext, n: PNode, flags: TExprFlags = {}): PNode +proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}, expectedType: PType = nil): PNode +proc semExprWithType(c: PContext, n: PNode, flags: TExprFlags = {}, expectedType: PType = nil): PNode proc semExprNoType(c: PContext, n: PNode): PNode proc semExprNoDeref(c: PContext, n: PNode, flags: TExprFlags = {}): PNode -proc semProcBody(c: PContext, n: PNode): PNode +proc semProcBody(c: PContext, n: PNode; expectedType: PType = nil): PNode proc fitNode(c: PContext, formal: PType, arg: PNode; info: TLineInfo): PNode proc changeType(c: PContext; n: PNode, newType: PType, check: bool) @@ -48,7 +51,7 @@ proc semQuoteAst(c: PContext, n: PNode): PNode proc finishMethod(c: PContext, s: PSym) proc evalAtCompileTime(c: PContext, n: PNode): PNode proc indexTypesMatch(c: PContext, f, a: PType, arg: PNode): PNode -proc semStaticExpr(c: PContext, n: PNode): PNode +proc semStaticExpr(c: PContext, n: PNode; expectedType: PType = nil): PNode proc semStaticType(c: PContext, childNode: PNode, prev: PType): PType proc semTypeOf(c: PContext; n: PNode): PNode proc computeRequiresInit(c: PContext, t: PType): bool @@ -269,12 +272,12 @@ proc paramsTypeCheck(c: PContext, typ: PType) {.inline.} = typeAllowedCheck(c, typ.n.info, typ, skProc) proc expectMacroOrTemplateCall(c: PContext, n: PNode): PSym -proc semDirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode +proc semDirectOp(c: PContext, n: PNode, flags: TExprFlags; expectedType: PType = nil): PNode proc semWhen(c: PContext, n: PNode, semCheck: bool = true): PNode proc semTemplateExpr(c: PContext, n: PNode, s: PSym, - flags: TExprFlags = {}): PNode + flags: TExprFlags = {}; expectedType: PType = nil): PNode proc semMacroExpr(c: PContext, n, nOrig: PNode, sym: PSym, - flags: TExprFlags = {}): PNode + flags: TExprFlags = {}; expectedType: PType = nil): PNode proc symFromType(c: PContext; t: PType, info: TLineInfo): PSym = if t.sym != nil: return t.sym @@ -334,8 +337,8 @@ proc fixupTypeAfterEval(c: PContext, evaluated, eOrig: PNode): PNode = isArrayConstr(arg): arg.typ = eOrig.typ -proc tryConstExpr(c: PContext, n: PNode): PNode = - var e = semExprWithType(c, n) +proc tryConstExpr(c: PContext, n: PNode; expectedType: PType = nil): PNode = + var e = semExprWithType(c, n, expectedType = expectedType) if e == nil: return result = getConstExpr(c.module, e, c.idgen, c.graph) @@ -365,8 +368,8 @@ proc tryConstExpr(c: PContext, n: PNode): PNode = const errConstExprExpected = "constant expression expected" -proc semConstExpr(c: PContext, n: PNode): PNode = - var e = semExprWithType(c, n) +proc semConstExpr(c: PContext, n: PNode; expectedType: PType = nil): PNode = + var e = semExprWithType(c, n, expectedType = expectedType) if e == nil: localError(c.config, n.info, errConstExprExpected) return n @@ -388,14 +391,14 @@ proc semConstExpr(c: PContext, n: PNode): PNode = else: result = fixupTypeAfterEval(c, result, e) -proc semExprFlagDispatched(c: PContext, n: PNode, flags: TExprFlags): PNode = +proc semExprFlagDispatched(c: PContext, n: PNode, flags: TExprFlags; expectedType: PType = nil): PNode = if efNeedStatic in flags: if efPreferNilResult in flags: - return tryConstExpr(c, n) + return tryConstExpr(c, n, expectedType) else: - return semConstExpr(c, n) + return semConstExpr(c, n, expectedType) else: - result = semExprWithType(c, n, flags) + result = semExprWithType(c, n, flags, expectedType) if efPreferStatic in flags: var evaluated = getConstExpr(c.module, result, c.idgen, c.graph) if evaluated != nil: return evaluated @@ -414,7 +417,7 @@ proc resetSemFlag(n: PNode) = resetSemFlag(n[i]) proc semAfterMacroCall(c: PContext, call, macroResult: PNode, - s: PSym, flags: TExprFlags): PNode = + s: PSym, flags: TExprFlags; expectedType: PType = nil): PNode = ## Semantically check the output of a macro. ## This involves processes such as re-checking the macro output for type ## coherence, making sure that variables declared with 'let' aren't @@ -438,10 +441,10 @@ proc semAfterMacroCall(c: PContext, call, macroResult: PNode, case retType.kind of tyUntyped: # Not expecting a type here allows templates like in ``tmodulealias.in``. - result = semExpr(c, result, flags) + result = semExpr(c, result, flags, expectedType) of tyTyped: # More restrictive version. - result = semExprWithType(c, result, flags) + result = semExprWithType(c, result, flags, expectedType) of tyTypeDesc: if result.kind == nkStmtList: result.transitionSonsKind(nkStmtListType) var typ = semTypeNode(c, result, nil) @@ -465,7 +468,7 @@ proc semAfterMacroCall(c: PContext, call, macroResult: PNode, retType = generateTypeInstance(c, paramTypes, macroResult.info, retType) - result = semExpr(c, result, flags) + result = semExpr(c, result, flags, expectedType) result = fitNode(c, retType, result, result.info) #globalError(s.info, errInvalidParamKindX, typeToString(s.typ[0])) dec(c.config.evalTemplateCounter) @@ -476,7 +479,7 @@ const errFloatToString = "cannot convert '$1' to '$2'" proc semMacroExpr(c: PContext, n, nOrig: PNode, sym: PSym, - flags: TExprFlags = {}): PNode = + flags: TExprFlags = {}; expectedType: PType = nil): PNode = rememberExpansion(c, nOrig.info, sym) pushInfoContext(c.config, nOrig.info, sym.detailedInfo) @@ -496,7 +499,7 @@ proc semMacroExpr(c: PContext, n, nOrig: PNode, sym: PSym, # c.evalContext = c.createEvalContext(emStatic) result = evalMacroCall(c.module, c.idgen, c.graph, c.templInstCounter, n, nOrig, sym) if efNoSemCheck notin flags: - result = semAfterMacroCall(c, n, result, sym, flags) + result = semAfterMacroCall(c, n, result, sym, flags, expectedType) if c.config.macrosToExpand.hasKey(sym.name.s): message(c.config, nOrig.info, hintExpandMacro, renderTree(result)) result = wrapInComesFrom(nOrig.info, sym, result) @@ -507,7 +510,7 @@ proc forceBool(c: PContext, n: PNode): PNode = if result == nil: result = n proc semConstBoolExpr(c: PContext, n: PNode): PNode = - result = forceBool(c, semConstExpr(c, n)) + result = forceBool(c, semConstExpr(c, n, getSysType(c.graph, n.info, tyBool))) if result.kind != nkIntLit: localError(c.config, n.info, errConstExprExpected) diff --git a/compiler/semcall.nim b/compiler/semcall.nim index 36658d472d61..3dff744a3f63 100644 --- a/compiler/semcall.nim +++ b/compiler/semcall.nim @@ -154,6 +154,8 @@ proc effectProblem(f, a: PType; result: var string; c: PContext) = "proc with {.locks: 0.} to get extended error information." of efEffectsDelayed: result.add "\n The `.effectsOf` annotations differ." + of efTagsIllegal: + result.add "\n The `.forbids` requirements caught an illegal tag." when defined(drnim): if not c.graph.compatibleProps(c.graph, f, a): result.add "\n The `.requires` or `.ensures` properties are incompatible." @@ -372,22 +374,6 @@ proc resolveOverloads(c: PContext, n, orig: PNode, let overloadsState = result.state if overloadsState != csMatch: - if c.p != nil and c.p.selfSym != nil: - # we need to enforce semchecking of selfSym again because it - # might need auto-deref: - var hiddenArg = newSymNode(c.p.selfSym) - hiddenArg.typ = nil - n.sons.insert(hiddenArg, 1) - orig.sons.insert(hiddenArg, 1) - - pickBest(f) - - if result.state != csMatch: - n.sons.delete(1) - orig.sons.delete(1) - excl n.flags, nfExprCall - else: return - if nfDotField in n.flags: internalAssert c.config, f.kind == nkIdent and n.len >= 2 @@ -558,8 +544,8 @@ proc semResolvedCall(c: PContext, x: TCandidate, for s in instantiateGenericParamList(c, gp, x.bindings): case s.kind of skConst: - if not s.ast.isNil: - x.call.add s.ast + if not s.astdef.isNil: + x.call.add s.astdef else: x.call.add c.graph.emptyNode of skType: @@ -730,4 +716,4 @@ proc searchForBorrowProc(c: PContext, startScope: PScope, fn: PSym): PSym = result = nil elif result.magic in {mArrPut, mArrGet}: # cannot borrow these magics for now - result = nil \ No newline at end of file + result = nil diff --git a/compiler/semdata.nim b/compiler/semdata.nim index d7a4d2c1cac5..363b7e5a4b2c 100644 --- a/compiler/semdata.nim +++ b/compiler/semdata.nim @@ -36,7 +36,6 @@ type # statements owner*: PSym # the symbol this context belongs to resultSym*: PSym # the result symbol (if we are in a proc) - selfSym*: PSym # the 'self' symbol (if available) nestedLoopCounter*: int # whether we are in a loop or not nestedBlockCounter*: int # whether we are in a block or not next*: PProcCon # used for stacking procedure contexts @@ -121,10 +120,10 @@ type symMapping*: TIdTable # every gensym'ed symbol needs to be mapped # to some new symbol in a generic instantiation libs*: seq[PLib] # all libs used by this module - semConstExpr*: proc (c: PContext, n: PNode): PNode {.nimcall.} # for the pragmas - semExpr*: proc (c: PContext, n: PNode, flags: TExprFlags = {}): PNode {.nimcall.} + semConstExpr*: proc (c: PContext, n: PNode; expectedType: PType = nil): PNode {.nimcall.} # for the pragmas + semExpr*: proc (c: PContext, n: PNode, flags: TExprFlags = {}, expectedType: PType = nil): PNode {.nimcall.} semTryExpr*: proc (c: PContext, n: PNode, flags: TExprFlags = {}): PNode {.nimcall.} - semTryConstExpr*: proc (c: PContext, n: PNode): PNode {.nimcall.} + semTryConstExpr*: proc (c: PContext, n: PNode; expectedType: PType = nil): PNode {.nimcall.} computeRequiresInit*: proc (c: PContext, t: PType): bool {.nimcall.} hasUnresolvedArgs*: proc (c: PContext, n: PNode): bool @@ -149,7 +148,6 @@ type inParallelStmt*: int instTypeBoundOp*: proc (c: PContext; dc: PSym; t: PType; info: TLineInfo; op: TTypeAttachedOp; col: int): PSym {.nimcall.} - selfName*: PIdent cache*: IdentCache graph*: ModuleGraph signatures*: TStrTable diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 702b47929c7c..a0753010a2a2 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -26,7 +26,7 @@ const errUndeclaredFieldX = "undeclared field: '$1'" proc semTemplateExpr(c: PContext, n: PNode, s: PSym, - flags: TExprFlags = {}): PNode = + flags: TExprFlags = {}; expectedType: PType = nil): PNode = rememberExpansion(c, n.info, s) let info = getCallLineInfo(n) markUsed(c, info, s) @@ -36,7 +36,8 @@ proc semTemplateExpr(c: PContext, n: PNode, s: PSym, pushInfoContext(c.config, n.info, s.detailedInfo) result = evalTemplate(n, s, getCurrOwner(c), c.config, c.cache, c.templInstCounter, c.idgen, efFromHlo in flags) - if efNoSemCheck notin flags: result = semAfterMacroCall(c, n, result, s, flags) + if efNoSemCheck notin flags: + result = semAfterMacroCall(c, n, result, s, flags, expectedType) popInfoContext(c.config) # XXX: A more elaborate line info rewrite might be needed @@ -65,9 +66,9 @@ proc semOperand(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = renderTree(result, {renderNoComments})) result.typ = errorType(c) -proc semExprCheck(c: PContext, n: PNode, flags: TExprFlags): PNode = +proc semExprCheck(c: PContext, n: PNode, flags: TExprFlags, expectedType: PType = nil): PNode = rejectEmptyNode(n) - result = semExpr(c, n, flags+{efWantValue}) + result = semExpr(c, n, flags+{efWantValue}, expectedType) let isEmpty = result.kind == nkEmpty @@ -82,8 +83,8 @@ proc semExprCheck(c: PContext, n: PNode, flags: TExprFlags): PNode = # do not produce another redundant error message: result = errorNode(c, n) -proc semExprWithType(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = - result = semExprCheck(c, n, flags) +proc semExprWithType(c: PContext, n: PNode, flags: TExprFlags = {}, expectedType: PType = nil): PNode = + result = semExprCheck(c, n, flags, expectedType) if result.typ == nil and efInTypeof in flags: result.typ = c.voidType elif result.typ == nil or result.typ == c.enforceVoidContext: @@ -107,7 +108,7 @@ proc semSymGenericInstantiation(c: PContext, n: PNode, s: PSym): PNode = result = symChoice(c, n, s, scClosed) proc inlineConst(c: PContext, n: PNode, s: PSym): PNode {.inline.} = - result = copyTree(s.ast) + result = copyTree(s.astdef) if result.isNil: localError(c.config, n.info, "constant of type '" & typeToString(s.typ) & "' has no value") result = newSymNode(s) @@ -196,10 +197,10 @@ proc checkConvertible(c: PContext, targetTyp: PType, src: PNode): TConvStatus = else: discard -proc isCastable(c: PContext; dst, src: PType): bool = +proc isCastable(c: PContext; dst, src: PType, info: TLineInfo): bool = ## Checks whether the source type can be cast to the destination type. ## Casting is very unrestrictive; casts are allowed as long as - ## castDest.size >= src.size, and typeAllowed(dst, skParam) + ## dst.size >= src.size, and typeAllowed(dst, skParam) #const # castableTypeKinds = {tyInt, tyPtr, tyRef, tyCstring, tyString, # tySequence, tyPointer, tyNil, tyOpenArray, @@ -228,19 +229,24 @@ proc isCastable(c: PContext; dst, src: PType): bool = # Just assume the programmer knows what he is doing. return true if dstSize < 0: - result = false + return false elif srcSize < 0: - result = false + return false elif typeAllowed(dst, skParam, c) != nil: - result = false + return false elif dst.kind == tyProc and dst.callConv == ccClosure: - result = src.kind == tyProc and src.callConv == ccClosure + return src.kind == tyProc and src.callConv == ccClosure else: result = (dstSize >= srcSize) or (skipTypes(dst, abstractInst).kind in IntegralTypes) or (skipTypes(src, abstractInst-{tyTypeDesc}).kind in IntegralTypes) + if result and (dstSize > srcSize): + var warnMsg = "target type is larger than source type" + warnMsg.add("\n target type: '$1' ($2)" % [$dst, if dstSize == 1: "1 byte" else: $dstSize & " bytes"]) + warnMsg.add("\n source type: '$1' ($2)" % [$src, if srcSize == 1: "1 byte" else: $srcSize & " bytes"]) + message(conf, info, warnCastSizes, warnMsg) if result and src.kind == tyNil: - result = dst.size <= conf.target.ptrSize + return dst.size <= conf.target.ptrSize proc isSymChoice(n: PNode): bool {.inline.} = result = n.kind in nkSymChoices @@ -259,7 +265,7 @@ proc isOwnedSym(c: PContext; n: PNode): bool = let s = qualifiedLookUp(c, n, {}) result = s != nil and sfSystemModule in s.owner.flags and s.name.s == "owned" -proc semConv(c: PContext, n: PNode): PNode = +proc semConv(c: PContext, n: PNode; expectedType: PType = nil): PNode = if n.len != 2: localError(c.config, n.info, "a type conversion takes exactly one argument") return n @@ -275,7 +281,7 @@ proc semConv(c: PContext, n: PNode): PNode = else: targetType = targetType.base of tyStatic: - var evaluated = semStaticExpr(c, n[1]) + var evaluated = semStaticExpr(c, n[1], expectedType) if evaluated.kind == nkType or evaluated.typ.kind == tyTypeDesc: result = n result.typ = c.makeTypeDesc semStaticType(c, evaluated, nil) @@ -359,11 +365,8 @@ proc semCast(c: PContext, n: PNode): PNode = let castedExpr = semExprWithType(c, n[1]) if tfHasMeta in targetType.flags: localError(c.config, n[0].info, "cannot cast to a non concrete type: '$1'" % $targetType) - if not isCastable(c, targetType, castedExpr.typ): - let tar = $targetType - let alt = typeToString(targetType, preferDesc) - let msg = if tar != alt: tar & "=" & alt else: tar - localError(c.config, n.info, "expression cannot be cast to " & msg) + if not isCastable(c, targetType, castedExpr.typ, n.info): + localError(c.config, n.info, "expression cannot be cast to '$1'" % $targetType) result = newNodeI(nkCast, n.info) result.typ = targetType result.add copyTree(n[0]) @@ -581,21 +584,36 @@ proc arrayConstrType(c: PContext, n: PNode): PType = typ[0] = makeRangeType(c, 0, n.len - 1, n.info) result = typ -proc semArrayConstr(c: PContext, n: PNode, flags: TExprFlags): PNode = +proc semArrayConstr(c: PContext, n: PNode, flags: TExprFlags; expectedType: PType = nil): PNode = result = newNodeI(nkBracket, n.info) result.typ = newTypeS(tyArray, c) + var expectedElementType, expectedIndexType: PType = nil + if expectedType != nil: + let expected = expectedType.skipTypes(abstractRange-{tyDistinct}) + case expected.kind + of tyArray: + expectedIndexType = expected[0] + expectedElementType = expected[1] + of tyOpenArray: + expectedElementType = expected[0] + else: discard rawAddSon(result.typ, nil) # index type var firstIndex, lastIndex: Int128 indexType = getSysType(c.graph, n.info, tyInt) lastValidIndex = lastOrd(c.config, indexType) if n.len == 0: - rawAddSon(result.typ, newTypeS(tyEmpty, c)) # needs an empty basetype! + rawAddSon(result.typ, + if expectedElementType != nil and + typeAllowed(expectedElementType, skLet, c) == nil: + expectedElementType + else: + newTypeS(tyEmpty, c)) # needs an empty basetype! lastIndex = toInt128(-1) else: var x = n[0] if x.kind == nkExprColonExpr and x.len == 2: - var idx = semConstExpr(c, x[0]) + var idx = semConstExpr(c, x[0], expectedIndexType) if not isOrdinalType(idx.typ): localError(c.config, idx.info, "expected ordinal value for array " & "index, got '$1'" % renderTree(idx)) @@ -606,8 +624,10 @@ proc semArrayConstr(c: PContext, n: PNode, flags: TExprFlags): PNode = lastValidIndex = lastOrd(c.config, indexType) x = x[1] - let yy = semExprWithType(c, x) + let yy = semExprWithType(c, x, expectedType = expectedElementType) var typ = yy.typ + if expectedElementType == nil: + expectedElementType = typ result.add yy #var typ = skipTypes(result[0].typ, {tyGenericInst, tyVar, tyLent, tyOrdinal}) for i in 1.. MaxSetElements: typ = makeRangeType(c, 0, MaxSetElements-1, n.info) @@ -2485,7 +2521,7 @@ proc semSetConstr(c: PContext, n: PNode): PNode = m = fitNode(c, typ, n[i], info) result.add m -proc semTableConstr(c: PContext, n: PNode): PNode = +proc semTableConstr(c: PContext, n: PNode; expectedType: PType = nil): PNode = # we simply transform ``{key: value, key2, key3: value}`` to # ``[(key, value), (key2, value2), (key3, value2)]`` result = newNodeI(nkBracket, n.info) @@ -2507,7 +2543,7 @@ proc semTableConstr(c: PContext, n: PNode): PNode = lastKey = i+1 if lastKey != n.len: illFormedAst(n, c.config) - result = semExpr(c, result) + result = semExpr(c, result, expectedType = expectedType) type TParKind = enum @@ -2534,8 +2570,13 @@ proc checkPar(c: PContext; n: PNode): TParKind = localError(c.config, n[i].info, errNamedExprNotAllowed) return paNone -proc semTupleFieldsConstr(c: PContext, n: PNode, flags: TExprFlags): PNode = +proc semTupleFieldsConstr(c: PContext, n: PNode, flags: TExprFlags; expectedType: PType = nil): PNode = result = newNodeI(nkTupleConstr, n.info) + var expected: PType = nil + if expectedType != nil: + expected = expectedType.skipTypes(abstractRange-{tyDistinct}) + if not (expected.kind == tyTuple and expected.len == n.len): + expected = nil var typ = newTypeS(tyTuple, c) typ.n = newNodeI(nkRecList, n.info) # nkIdentDefs var ids = initIntSet() @@ -2545,7 +2586,9 @@ proc semTupleFieldsConstr(c: PContext, n: PNode, flags: TExprFlags): PNode = let id = considerQuotedIdent(c, n[i][0]) if containsOrIncl(ids, id.id): localError(c.config, n[i].info, errFieldInitTwice % id.s) - n[i][1] = semExprWithType(c, n[i][1], {}) + # can check if field name matches expected type here + let expectedElemType = if expected != nil: expected[i] else: nil + n[i][1] = semExprWithType(c, n[i][1], {}, expectedElemType) if n[i][1].typ.kind == tyTypeDesc: localError(c.config, n[i][1].info, "typedesc not allowed as tuple field.") @@ -2560,18 +2603,24 @@ proc semTupleFieldsConstr(c: PContext, n: PNode, flags: TExprFlags): PNode = result.add n[i] result.typ = typ -proc semTuplePositionsConstr(c: PContext, n: PNode, flags: TExprFlags): PNode = +proc semTuplePositionsConstr(c: PContext, n: PNode, flags: TExprFlags; expectedType: PType = nil): PNode = result = n # we don't modify n, but compute the type: result.transitionSonsKind(nkTupleConstr) + var expected: PType = nil + if expectedType != nil: + expected = expectedType.skipTypes(abstractRange-{tyDistinct}) + if not (expected.kind == tyTuple and expected.len == n.len): + expected = nil var typ = newTypeS(tyTuple, c) # leave typ.n nil! for i in 0.. 0: # don't interpret () as type isTupleType = tupexp[0].typ.kind == tyTypeDesc @@ -2749,7 +2798,7 @@ proc enumFieldSymChoice(c: PContext, n: PNode, s: PSym): PNode = var i = 0 var a = initOverloadIter(o, c, n) while a != nil: - if a.kind in OverloadableSyms-{skModule}: + if a.kind == skEnumField: inc(i) if i > 1: break a = nextOverloadIter(o, c, n) @@ -2765,7 +2814,7 @@ proc enumFieldSymChoice(c: PContext, n: PNode, s: PSym): PNode = result = newNodeIT(nkClosedSymChoice, info, newTypeS(tyNone, c)) a = initOverloadIter(o, c, n) while a != nil: - if a.kind in OverloadableSyms-{skModule}: + if a.kind == skEnumField: incl(a.flags, sfUsed) markOwnerModuleAsUsed(c, a) result.add newSymNode(a, info) @@ -2778,7 +2827,7 @@ proc semPragmaStmt(c: PContext; n: PNode) = else: pragma(c, c.p.owner, n, stmtPragmas, true) -proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = +proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}, expectedType: PType = nil): PNode = when defined(nimCompilerStacktraceHints): setFrameMsg c.config$n.info & " " & $n.kind when false: # see `tdebugutils` @@ -2788,18 +2837,38 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = if isCompilerDebug(): echo ("<", c.config$n.info, n, ?.result.typ) + template directLiteral(typeKind: TTypeKind) = + if result.typ == nil: + if expectedType != nil and ( + let expected = expectedType.skipTypes(abstractRange-{tyDistinct}); + expected.kind == typeKind): + result.typ = expected + changeType(c, result, expectedType, check=true) + else: + result.typ = getSysType(c.graph, n.info, typeKind) + result = n if c.config.cmd == cmdIdeTools: suggestExpr(c, n) if nfSem in n.flags: return case n.kind of nkIdent, nkAccQuoted: - let checks = if efNoEvaluateGeneric in flags: - {checkUndeclared, checkPureEnumFields} - elif efInCall in flags: - {checkUndeclared, checkModule, checkPureEnumFields} - else: - {checkUndeclared, checkModule, checkAmbiguity, checkPureEnumFields} - var s = qualifiedLookUp(c, n, checks) + var s: PSym + if expectedType != nil and ( + let expected = expectedType.skipTypes(abstractRange-{tyDistinct}); + expected.kind == tyEnum): + let nameId = considerQuotedIdent(c, n).id + for f in expected.n: + if f.kind == nkSym and f.sym.name.id == nameId: + s = f.sym + break + if s == nil: + let checks = if efNoEvaluateGeneric in flags: + {checkUndeclared, checkPureEnumFields} + elif efInCall in flags: + {checkUndeclared, checkModule, checkPureEnumFields} + else: + {checkUndeclared, checkModule, checkAmbiguity, checkPureEnumFields} + s = qualifiedLookUp(c, n, checks) if c.matchedConcept == nil: semCaptureSym(s, c.p.owner) case s.kind of skProc, skFunc, skMethod, skConverter, skIterator: @@ -2813,12 +2882,13 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = if optOwnedRefs in c.config.globalOptions: result.typ = makeVarType(c, result.typ, tyOwned) of skEnumField: - if overloadableEnums in c.features: - result = enumFieldSymChoice(c, n, s) - else: - result = semSym(c, n, s, flags) + result = enumFieldSymChoice(c, n, s) else: result = semSym(c, n, s, flags) + if expectedType != nil and isSymChoice(result): + result = fitNode(c, expectedType, result, n.info) + if result.kind == nkSym: + result = semSym(c, result, result.sym, flags) of nkSym: # because of the changed symbol binding, this does not mean that we # don't have to check the symbol for semantics here again! @@ -2826,39 +2896,56 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = of nkEmpty, nkNone, nkCommentStmt, nkType: discard of nkNilLit: - if result.typ == nil: result.typ = getNilType(c) + if result.typ == nil: + result.typ = getNilType(c) + if expectedType != nil: + var m = newCandidate(c, result.typ) + if typeRel(m, expectedType, result.typ) >= isSubtype: + result.typ = expectedType + # or: result = fitNode(c, expectedType, result, n.info) of nkIntLit: - if result.typ == nil: setIntLitType(c, result) - of nkInt8Lit: - if result.typ == nil: result.typ = getSysType(c.graph, n.info, tyInt8) - of nkInt16Lit: - if result.typ == nil: result.typ = getSysType(c.graph, n.info, tyInt16) - of nkInt32Lit: - if result.typ == nil: result.typ = getSysType(c.graph, n.info, tyInt32) - of nkInt64Lit: - if result.typ == nil: result.typ = getSysType(c.graph, n.info, tyInt64) - of nkUIntLit: - if result.typ == nil: result.typ = getSysType(c.graph, n.info, tyUInt) - of nkUInt8Lit: - if result.typ == nil: result.typ = getSysType(c.graph, n.info, tyUInt8) - of nkUInt16Lit: - if result.typ == nil: result.typ = getSysType(c.graph, n.info, tyUInt16) - of nkUInt32Lit: - if result.typ == nil: result.typ = getSysType(c.graph, n.info, tyUInt32) - of nkUInt64Lit: - if result.typ == nil: result.typ = getSysType(c.graph, n.info, tyUInt64) - #of nkFloatLit: - # if result.typ == nil: result.typ = getFloatLitType(result) - of nkFloat32Lit: - if result.typ == nil: result.typ = getSysType(c.graph, n.info, tyFloat32) - of nkFloat64Lit, nkFloatLit: - if result.typ == nil: result.typ = getSysType(c.graph, n.info, tyFloat64) - of nkFloat128Lit: - if result.typ == nil: result.typ = getSysType(c.graph, n.info, tyFloat128) + if result.typ == nil: + if expectedType != nil and ( + let expected = expectedType.skipTypes(abstractRange-{tyDistinct}); + expected.kind in {tyInt..tyInt64, + tyUInt..tyUInt64, + tyFloat..tyFloat128}): + result.typ = expected + if expected.kind in {tyFloat..tyFloat128}: + n.transitionIntToFloatKind(nkFloatLit) + changeType(c, result, expectedType, check=true) + else: + setIntLitType(c, result) + of nkInt8Lit: directLiteral(tyInt8) + of nkInt16Lit: directLiteral(tyInt16) + of nkInt32Lit: directLiteral(tyInt32) + of nkInt64Lit: directLiteral(tyInt64) + of nkUIntLit: directLiteral(tyUInt) + of nkUInt8Lit: directLiteral(tyUInt8) + of nkUInt16Lit: directLiteral(tyUInt16) + of nkUInt32Lit: directLiteral(tyUInt32) + of nkUInt64Lit: directLiteral(tyUInt64) + of nkFloatLit: + if result.typ == nil: + if expectedType != nil and ( + let expected = expectedType.skipTypes(abstractRange-{tyDistinct}); + expected.kind in {tyFloat..tyFloat128}): + result.typ = expected + changeType(c, result, expectedType, check=true) + else: + result.typ = getSysType(c.graph, n.info, tyFloat64) + of nkFloat32Lit: directLiteral(tyFloat32) + of nkFloat64Lit: directLiteral(tyFloat64) + of nkFloat128Lit: directLiteral(tyFloat128) of nkStrLit..nkTripleStrLit: - if result.typ == nil: result.typ = getSysType(c.graph, n.info, tyString) - of nkCharLit: - if result.typ == nil: result.typ = getSysType(c.graph, n.info, tyChar) + if result.typ == nil: + if expectedType != nil and ( + let expected = expectedType.skipTypes(abstractRange-{tyDistinct}); + expected.kind in {tyString, tyCstring}): + result.typ = expectedType + else: + result.typ = getSysType(c.graph, n.info, tyString) + of nkCharLit: directLiteral(tyChar) of nkDotExpr: result = semFieldAccess(c, n, flags) if result.kind == nkDotCall: @@ -2866,7 +2953,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = result = semExpr(c, result, flags) of nkBind: message(c.config, n.info, warnDeprecated, "bind is deprecated") - result = semExpr(c, n[0], flags) + result = semExpr(c, n[0], flags, expectedType) of nkTypeOfExpr..nkTupleClassTy, nkStaticTy, nkRefTy..nkEnumTy: if c.matchedConcept != nil and n.len == 1: let modifier = n.modifierTypeKindOfNode @@ -2892,40 +2979,40 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = # pretty.checkUse(n[0][1].info, s) case s.kind of skMacro, skTemplate: - result = semDirectOp(c, n, flags) + result = semDirectOp(c, n, flags, expectedType) of skType: # XXX think about this more (``set`` procs) let ambig = c.isAmbiguous if not (n[0].kind in {nkClosedSymChoice, nkOpenSymChoice, nkIdent} and ambig) and n.len == 2: - result = semConv(c, n) + result = semConv(c, n, expectedType) elif ambig and n.len == 1: errorUseQualifier(c, n.info, s) elif n.len == 1: - result = semObjConstr(c, n, flags) - elif s.magic == mNone: result = semDirectOp(c, n, flags) - else: result = semMagic(c, n, s, flags) + result = semObjConstr(c, n, flags, expectedType) + elif s.magic == mNone: result = semDirectOp(c, n, flags, expectedType) + else: result = semMagic(c, n, s, flags, expectedType) of skProc, skFunc, skMethod, skConverter, skIterator: if s.magic == mNone: result = semDirectOp(c, n, flags) - else: result = semMagic(c, n, s, flags) + else: result = semMagic(c, n, s, flags, expectedType) else: #liMessage(n.info, warnUser, renderTree(n)); - result = semIndirectOp(c, n, flags) + result = semIndirectOp(c, n, flags, expectedType) elif (n[0].kind == nkBracketExpr or shouldBeBracketExpr(n)) and isSymChoice(n[0][0]): # indirectOp can deal with explicit instantiations; the fixes # the 'newSeq[T](x)' bug setGenericParams(c, n[0]) - result = semDirectOp(c, n, flags) + result = semDirectOp(c, n, flags, expectedType) elif nfDotField in n.flags: - result = semDirectOp(c, n, flags) + result = semDirectOp(c, n, flags, expectedType) elif isSymChoice(n[0]): let b = asBracketExpr(c, n) if b != nil: - result = semExpr(c, b, flags) + result = semExpr(c, b, flags, expectedType) else: - result = semDirectOp(c, n, flags) + result = semDirectOp(c, n, flags, expectedType) else: - result = semIndirectOp(c, n, flags) + result = semIndirectOp(c, n, flags, expectedType) if nfDefaultRefsParam in result.flags: result = result.copyTree #XXX: Figure out what causes default param nodes to be shared.. (sigmatch bug?) @@ -2944,12 +3031,12 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = # This is a "when nimvm" stmt. result = semWhen(c, n, true) else: - result = semExpr(c, result, flags) + result = semExpr(c, result, flags, expectedType) of nkBracketExpr: checkMinSonsLen(n, 1, c.config) - result = semArrayAccess(c, n, flags) + result = semArrayAccess(c, n, flags, expectedType) of nkCurlyExpr: - result = semExpr(c, buildOverloadedSubscripts(n, getIdent(c.cache, "{}")), flags) + result = semExpr(c, buildOverloadedSubscripts(n, getIdent(c.cache, "{}")), flags, expectedType) of nkPragmaExpr: var pragma = n[1] @@ -2971,12 +3058,12 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = of nkPar, nkTupleConstr: case checkPar(c, n) of paNone: result = errorNode(c, n) - of paTuplePositions: result = semTupleConstr(c, n, flags) - of paTupleFields: result = semTupleFieldsConstr(c, n, flags) - of paSingle: result = semExpr(c, n[0], flags) - of nkCurly: result = semSetConstr(c, n) - of nkBracket: result = semArrayConstr(c, n, flags) - of nkObjConstr: result = semObjConstr(c, n, flags) + of paTuplePositions: result = semTupleConstr(c, n, flags, expectedType) + of paTupleFields: result = semTupleFieldsConstr(c, n, flags, expectedType) + of paSingle: result = semExpr(c, n[0], flags, expectedType) + of nkCurly: result = semSetConstr(c, n, expectedType) + of nkBracket: result = semArrayConstr(c, n, flags, expectedType) + of nkObjConstr: result = semObjConstr(c, n, flags, expectedType) of nkLambdaKinds: result = semProcAux(c, n, skProc, lambdaPragmas, flags) of nkDerefExpr: result = semDeref(c, n) of nkAddr: @@ -2986,9 +3073,9 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = result.typ = makePtrType(c, result[0].typ) of nkHiddenAddr, nkHiddenDeref: checkSonsLen(n, 1, c.config) - n[0] = semExpr(c, n[0], flags) + n[0] = semExpr(c, n[0], flags, expectedType) of nkCast: result = semCast(c, n) - of nkIfExpr, nkIfStmt: result = semIf(c, n, flags) + of nkIfExpr, nkIfStmt: result = semIf(c, n, flags, expectedType) of nkHiddenStdConv, nkHiddenSubConv, nkConv, nkHiddenCallConv: checkSonsLen(n, 2, c.config) considerGenSyms(c, n) @@ -3002,15 +3089,15 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = checkMinSonsLen(n, 2, c.config) considerGenSyms(c, n) of nkTableConstr: - result = semTableConstr(c, n) + result = semTableConstr(c, n, expectedType) of nkClosedSymChoice, nkOpenSymChoice: # handling of sym choices is context dependent # the node is left intact for now discard - of nkStaticExpr: result = semStaticExpr(c, n[0]) + of nkStaticExpr: result = semStaticExpr(c, n[0], expectedType) of nkAsgn: result = semAsgn(c, n) - of nkBlockStmt, nkBlockExpr: result = semBlock(c, n, flags) - of nkStmtList, nkStmtListExpr: result = semStmtList(c, n, flags) + of nkBlockStmt, nkBlockExpr: result = semBlock(c, n, flags, expectedType) + of nkStmtList, nkStmtListExpr: result = semStmtList(c, n, flags, expectedType) of nkRaiseStmt: result = semRaise(c, n) of nkVarSection: result = semVarOrLet(c, n, skVar) of nkLetSection: result = semVarOrLet(c, n, skLet) @@ -3018,10 +3105,10 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = of nkTypeSection: result = semTypeSection(c, n) of nkDiscardStmt: result = semDiscard(c, n) of nkWhileStmt: result = semWhile(c, n, flags) - of nkTryStmt, nkHiddenTryStmt: result = semTry(c, n, flags) + of nkTryStmt, nkHiddenTryStmt: result = semTry(c, n, flags, expectedType) of nkBreakStmt, nkContinueStmt: result = semBreakOrContinue(c, n) of nkForStmt, nkParForStmt: result = semFor(c, n, flags) - of nkCaseStmt: result = semCase(c, n, flags) + of nkCaseStmt: result = semCase(c, n, flags, expectedType) of nkReturnStmt: result = semReturn(c, n) of nkUsingStmt: result = semUsing(c, n) of nkAsmStmt: result = semAsm(c, n) @@ -3060,7 +3147,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = if not isTopLevel(c): localError(c.config, n.info, errXOnlyAtModuleScope % "export") result = semExportExcept(c, n) of nkPragmaBlock: - result = semPragmaBlock(c, n) + result = semPragmaBlock(c, n, expectedType) of nkStaticStmt: result = semStaticStmt(c, n) of nkDefer: diff --git a/compiler/semfold.nim b/compiler/semfold.nim index d2bbc63be123..94e5fda314eb 100644 --- a/compiler/semfold.nim +++ b/compiler/semfold.nim @@ -18,7 +18,7 @@ import from system/memory import nimCStrLen when defined(nimPreviewSlimSystem): - import std/assertions + import std/[assertions, formatfloat] proc errorType*(g: ModuleGraph): PType = ## creates a type representing an error state @@ -409,7 +409,7 @@ proc foldConv(n, a: PNode; idgen: IdGenerator; g: ModuleGraph; check = false): P rangeCheck(n, getInt(result), g) of tyFloat..tyFloat64: case srcTyp.kind - of tyInt..tyInt64, tyEnum, tyBool, tyChar: + of tyInt..tyInt64, tyUInt..tyUInt64, tyEnum, tyBool, tyChar: result = newFloatNodeT(toFloat64(getOrdValue(a)), n, g) else: result = a @@ -517,12 +517,12 @@ proc getConstExpr(m: PSym, n: PNode; idgen: IdGenerator; g: ModuleGraph): PNode "{.intdefine.} const was set to an invalid integer: '" & g.config.symbols[s.name.s] & "'") else: - result = copyTree(s.ast) + result = copyTree(s.astdef) of mStrDefine: if isDefined(g.config, s.name.s): result = newStrNodeT(g.config.symbols[s.name.s], n, g) else: - result = copyTree(s.ast) + result = copyTree(s.astdef) of mBoolDefine: if isDefined(g.config, s.name.s): try: @@ -532,9 +532,9 @@ proc getConstExpr(m: PSym, n: PNode; idgen: IdGenerator; g: ModuleGraph): PNode "{.booldefine.} const was set to an invalid bool: '" & g.config.symbols[s.name.s] & "'") else: - result = copyTree(s.ast) + result = copyTree(s.astdef) else: - result = copyTree(s.ast) + result = copyTree(s.astdef) of skProc, skFunc, skMethod: result = n of skParam: diff --git a/compiler/semgnrc.nim b/compiler/semgnrc.nim index e249d88e8208..d1c4acef683a 100644 --- a/compiler/semgnrc.nim +++ b/compiler/semgnrc.nim @@ -108,11 +108,7 @@ proc semGenericStmtSymbol(c: PContext, n: PNode, s: PSym, result = n onUse(n.info, s) of skEnumField: - if overloadableEnums in c.features: - result = symChoice(c, n, s, scOpen) - else: - result = newSymNode(s, n.info) - onUse(n.info, s) + result = symChoice(c, n, s, scOpen) else: result = newSymNode(s, n.info) onUse(n.info, s) @@ -179,7 +175,7 @@ proc fuzzyLookup(c: PContext, n: PNode, flags: TSemGenericFlags, proc addTempDecl(c: PContext; n: PNode; kind: TSymKind) = let s = newSymS(skUnknown, getIdentNode(c, n), c) addPrelimDecl(c, s) - styleCheckDef(c.config, n.info, s, kind) + styleCheckDef(c, n.info, s, kind) onDef(n.info, s) proc semGenericStmt(c: PContext, n: PNode, diff --git a/compiler/seminst.nim b/compiler/seminst.nim index 6fae0583d0f9..bd5eb1ec319c 100644 --- a/compiler/seminst.nim +++ b/compiler/seminst.nim @@ -28,32 +28,13 @@ proc addObjFieldsToLocalScope(c: PContext; n: PNode) = # it is not an error to shadow fields via parameters else: discard -proc rawPushProcCon(c: PContext, owner: PSym) = +proc pushProcCon*(c: PContext; owner: PSym) = var x: PProcCon new(x) x.owner = owner x.next = c.p c.p = x -proc rawHandleSelf(c: PContext; owner: PSym) = - const callableSymbols = {skProc, skFunc, skMethod, skConverter, skIterator, skMacro} - if c.selfName != nil and owner.kind in callableSymbols and owner.typ != nil: - let params = owner.typ.n - if params.len > 1: - let arg = params[1].sym - if arg.name.id == c.selfName.id: - c.p.selfSym = arg - arg.flags.incl sfIsSelf - var t = c.p.selfSym.typ.skipTypes(abstractPtrs) - while t.kind == tyObject: - addObjFieldsToLocalScope(c, t.n) - if t[0] == nil: break - t = t[0].skipTypes(skipPtrs) - -proc pushProcCon*(c: PContext; owner: PSym) = - rawPushProcCon(c, owner) - rawHandleSelf(c, owner) - const errCannotInstantiateX = "cannot instantiate: '$1'" @@ -73,7 +54,8 @@ iterator instantiateGenericParamList(c: PContext, n: PNode, pt: TIdTable): PSym # later by semAsgn in return type inference scenario t = q.typ else: - localError(c.config, a.info, errCannotInstantiateX % s.name.s) + if q.typ.kind != tyCompositeTypeClass: + localError(c.config, a.info, errCannotInstantiateX % s.name.s) t = errorType(c) elif t.kind in {tyGenericParam, tyConcept}: localError(c.config, a.info, errCannotInstantiateX % q.name.s) @@ -377,7 +359,7 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable, addDecl(c, s) entry.concreteTypes[i] = s.typ inc i - rawPushProcCon(c, result) + pushProcCon(c, result) instantiateProcType(c, pt, result, info) for j in 1..= toCover: tracked.init.add id # else we can't merge setLen(tracked.guards.s, oldFacts) + dec tracked.inIfStmt proc trackIf(tracked: PEffects, n: PNode) = track(tracked, n[0][0]) + inc tracked.inIfStmt let oldFacts = tracked.guards.s.len addFact(tracked.guards, n[0][0]) let oldState = tracked.init.len @@ -731,6 +743,7 @@ proc trackIf(tracked: PEffects, n: PNode) = if count >= toCover: tracked.init.add id # else we can't merge as it is not exhaustive setLen(tracked.guards.s, oldFacts) + dec tracked.inIfStmt proc trackBlock(tracked: PEffects, n: PNode) = if n.kind in {nkStmtList, nkStmtListExpr}: @@ -810,6 +823,10 @@ proc passedToEffectsDelayedParam(tracked: PEffects; n: PNode) = markSideEffect(tracked, n, n.info) ]# +proc checkForSink(tracked: PEffects; n: PNode) = + if tracked.inIfStmt == 0: + checkForSink(tracked.config, tracked.c.idgen, tracked.owner, n) + proc trackCall(tracked: PEffects; n: PNode) = template gcsafeAndSideeffectCheck() = if notGcSafe(op) and not importedFromC(a): @@ -909,7 +926,7 @@ proc trackCall(tracked: PEffects; n: PNode) = case op[i].kind of tySink: createTypeBoundOps(tracked, op[i][0], n.info) - checkForSink(tracked.config, tracked.c.idgen, tracked.owner, n[i]) + checkForSink(tracked, n[i]) of tyVar: tracked.hasDangerousAssign = true #of tyOut: @@ -924,14 +941,17 @@ type oldLocked: int oldLockLevel: TLockLevel enforcedGcSafety, enforceNoSideEffects: bool - oldExc, oldTags: int - exc, tags: PNode + oldExc, oldTags, oldForbids: int + exc, tags, forbids: PNode proc createBlockContext(tracked: PEffects): PragmaBlockContext = + var oldForbidsLen = 0 + if tracked.forbids != nil: oldForbidsLen = tracked.forbids.len result = PragmaBlockContext(oldLocked: tracked.locked.len, oldLockLevel: tracked.currLockLevel, enforcedGcSafety: false, enforceNoSideEffects: false, - oldExc: tracked.exc.len, oldTags: tracked.tags.len) + oldExc: tracked.exc.len, oldTags: tracked.tags.len, + oldForbids: oldForbidsLen) proc applyBlockContext(tracked: PEffects, bc: PragmaBlockContext) = if bc.enforcedGcSafety: tracked.inEnforcedGcSafe = true @@ -952,6 +972,10 @@ proc unapplyBlockContext(tracked: PEffects; bc: PragmaBlockContext) = setLen(tracked.tags.sons, bc.oldTags) for t in bc.tags: addTag(tracked, t, t) + if bc.forbids != nil: + setLen(tracked.forbids.sons, bc.oldForbids) + for t in bc.forbids: + addNotTag(tracked, t, t) proc castBlock(tracked: PEffects, pragma: PNode, bc: var PragmaBlockContext) = case whichPragma(pragma) @@ -966,6 +990,13 @@ proc castBlock(tracked: PEffects, pragma: PNode, bc: var PragmaBlockContext) = else: bc.tags = newNodeI(nkArgList, pragma.info) bc.tags.add n + of wForbids: + let n = pragma[1] + if n.kind in {nkCurly, nkBracket}: + bc.forbids = n + else: + bc.forbids = newNodeI(nkArgList, pragma.info) + bc.forbids.add n of wRaises: let n = pragma[1] if n.kind in {nkCurly, nkBracket}: @@ -1057,7 +1088,7 @@ proc track(tracked: PEffects, n: PNode) = if tracked.owner.kind != skMacro and n[0].typ.kind notin {tyOpenArray, tyVarargs}: createTypeBoundOps(tracked, n[0].typ, n.info) if n[0].kind != nkSym or not isLocalVar(tracked, n[0].sym): - checkForSink(tracked.config, tracked.c.idgen, tracked.owner, n[1]) + checkForSink(tracked, n[1]) if not tracked.hasDangerousAssign and n[0].kind != nkSym: tracked.hasDangerousAssign = true of nkVarSection, nkLetSection: @@ -1070,12 +1101,13 @@ proc track(tracked: PEffects, n: PNode) = for i in 0..= 1: it[0] else: it - + when false: let lhs = b[0] let clash = strTableGet(c.currentScope.symbols, lhs.ident) @@ -595,7 +609,7 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode = var def: PNode = c.graph.emptyNode if a[^1].kind != nkEmpty: - def = semExprWithType(c, a[^1], {}) + def = semExprWithType(c, a[^1], {}, typ) if def.kind in nkSymChoices and def[0].typ.skipTypes(abstractInst).kind == tyEnum: errorSymChoiceUseQualifier(c, def) @@ -662,7 +676,7 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode = addToVarSection(c, result, n, a) continue var v = semIdentDef(c, a[j], symkind, false) - styleCheckDef(c.config, v) + styleCheckDef(c, v) onDef(a[j].info, v) if sfGenSym notin v.flags: if not isDiscardUnderscore(v): addInterfaceDecl(c, v) @@ -680,28 +694,25 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode = if def.kind != nkEmpty: if sfThread in v.flags: localError(c.config, def.info, errThreadvarCannotInit) setVarType(c, v, typ) + # this is needed for the evaluation pass, guard checking + # and custom pragmas: b = newNodeI(nkIdentDefs, a.info) if importantComments(c.config): # keep documentation information: b.comment = a.comment - b.add newSymNode(v) - # keep type desc for doc generator - b.add a[^2] - b.add copyTree(def) - addToVarSection(c, result, n, b) - # this is needed for the evaluation pass, guard checking - # and custom pragmas: - var ast = newNodeI(nkIdentDefs, a.info) + # postfix not generated here (to generate, get rid of it in transf) if a[j].kind == nkPragmaExpr: var p = newNodeI(nkPragmaExpr, a.info) p.add newSymNode(v) - p.add a[j][1].copyTree - ast.add p + p.add a[j][1] + b.add p else: - ast.add newSymNode(v) - ast.add a[^2].copyTree - ast.add def - v.ast = ast + b.add newSymNode(v) + # keep type desc for doc generator + b.add a[^2] + b.add copyTree(def) + addToVarSection(c, result, n, b) + v.ast = b else: if def.kind in {nkPar, nkTupleConstr}: v.ast = def[j] # bug #7663, for 'nim check' this can be a non-tuple: @@ -751,7 +762,7 @@ proc semConst(c: PContext, n: PNode): PNode = var typFlags: TTypeAllowedFlags # don't evaluate here since the type compatibility check below may add a converter - var def = semExprWithType(c, a[^1]) + var def = semExprWithType(c, a[^1], {}, typ) if def.kind == nkSym and def.sym.kind in {skTemplate, skMacro}: typFlags.incl taIsTemplateOrMacro @@ -792,17 +803,26 @@ proc semConst(c: PContext, n: PNode): PNode = var v = semIdentDef(c, a[j], skConst) if sfGenSym notin v.flags: addInterfaceDecl(c, v) elif v.owner == nil: v.owner = getCurrOwner(c) - styleCheckDef(c.config, v) + styleCheckDef(c, v) onDef(a[j].info, v) if a.kind != nkVarTuple: setVarType(c, v, typ) - v.ast = def # no need to copy + when false: + v.ast = def # no need to copy b = newNodeI(nkConstDef, a.info) if importantComments(c.config): b.comment = a.comment - b.add newSymNode(v) + # postfix not generated here (to generate, get rid of it in transf) + if a[j].kind == nkPragmaExpr: + var p = newNodeI(nkPragmaExpr, a.info) + p.add newSymNode(v) + p.add a[j][1].copyTree + b.add p + else: + b.add newSymNode(v) b.add a[1] b.add copyTree(def) + v.ast = b else: setVarType(c, v, typ[j]) v.ast = if def[j].kind != nkExprColonExpr: def[j] @@ -817,7 +837,7 @@ include semfields proc symForVar(c: PContext, n: PNode): PSym = let m = if n.kind == nkPragmaExpr: n[0] else: n result = newSymG(skForVar, m, c) - styleCheckDef(c.config, result) + styleCheckDef(c, result) onDef(n.info, result) if n.kind == nkPragmaExpr: pragma(c, result, n[1], forVarPragmas) @@ -1037,7 +1057,7 @@ proc semFor(c: PContext, n: PNode; flags: TExprFlags): PNode = result.typ = result.lastSon.typ closeScope(c) -proc semCase(c: PContext, n: PNode; flags: TExprFlags): PNode = +proc semCase(c: PContext, n: PNode; flags: TExprFlags; expectedType: PType = nil): PNode = result = n checkMinSonsLen(n, 2, c.config) openScope(c) @@ -1046,16 +1066,17 @@ proc semCase(c: PContext, n: PNode; flags: TExprFlags): PNode = var chckCovered = false var covered: Int128 = toInt128(0) var typ = commonTypeBegin + var expectedType = expectedType var hasElse = false let caseTyp = skipTypes(n[0].typ, abstractVar-{tyTypeDesc}) - const shouldChckCovered = {tyInt..tyInt64, tyChar, tyEnum, tyUInt..tyUInt32, tyBool} + const shouldChckCovered = {tyInt..tyInt64, tyChar, tyEnum, tyUInt..tyUInt64, tyBool} case caseTyp.kind of shouldChckCovered: chckCovered = true of tyRange: if skipTypes(caseTyp[0], abstractInst).kind in shouldChckCovered: chckCovered = true - of tyFloat..tyFloat128, tyString, tyError: + of tyFloat..tyFloat128, tyString, tyCstring, tyError: discard else: popCaseContext(c) @@ -1077,20 +1098,23 @@ proc semCase(c: PContext, n: PNode; flags: TExprFlags): PNode = checkMinSonsLen(x, 2, c.config) semCaseBranch(c, n, x, i, covered) var last = x.len-1 - x[last] = semExprBranchScope(c, x[last]) + x[last] = semExprBranchScope(c, x[last], expectedType) typ = commonType(c, typ, x[last]) + expectedType = typ of nkElifBranch: chckCovered = false checkSonsLen(x, 2, c.config) openScope(c) - x[0] = forceBool(c, semExprWithType(c, x[0])) - x[1] = semExprBranch(c, x[1]) + x[0] = forceBool(c, semExprWithType(c, x[0], expectedType = getSysType(c.graph, n.info, tyBool))) + x[1] = semExprBranch(c, x[1], expectedType = expectedType) typ = commonType(c, typ, x[1]) + expectedType = typ closeScope(c) of nkElse: checkSonsLen(x, 1, c.config) - x[0] = semExprBranchScope(c, x[0]) + x[0] = semExprBranchScope(c, x[0], expectedType) typ = commonType(c, typ, x[0]) + expectedType = typ if (chckCovered and covered == toCover(c, n[0].typ)) or hasElse: message(c.config, x.info, warnUnreachableElse) hasElse = true @@ -1312,6 +1336,7 @@ proc typeSectionRightSidePass(c: PContext, n: PNode) = if s.magic == mNone and a[2].kind == nkEmpty: localError(c.config, a.info, errImplOfXexpected % s.name.s) if s.magic != mNone: processMagicType(c, s) + let oldFlags = s.typ.flags if a[1].kind != nkEmpty: # We have a generic type declaration here. In generic types, # symbol lookup needs to be done here. @@ -1339,6 +1364,13 @@ proc typeSectionRightSidePass(c: PContext, n: PNode) = if body != nil: body.sym = s body.size = -1 # could not be computed properly + if body.kind == tyObject: + # add flags applied to generic type to object (nominal) type + incl(body.flags, oldFlags) + # {.inheritable, final.} is already disallowed, but + # object might have been assumed to be final + if tfInheritable in oldFlags and tfFinal in body.flags: + excl(body.flags, tfFinal) s.typ[^1] = body if tfCovariant in s.typ.flags: checkCovariantParamsUsages(c, s.typ) @@ -1390,8 +1422,16 @@ proc typeSectionRightSidePass(c: PContext, n: PNode) = internalAssert c.config, st.kind in {tyPtr, tyRef} internalAssert c.config, st.lastSon.sym == nil incl st.flags, tfRefsAnonObj + let objTy = st.lastSon + # add flags for `ref object` etc to underlying `object` + incl(objTy.flags, oldFlags) + # {.inheritable, final.} is already disallowed, but + # object might have been assumed to be final + if tfInheritable in oldFlags and tfFinal in objTy.flags: + excl(objTy.flags, tfFinal) let obj = newSym(skType, getIdent(c.cache, s.name.s & ":ObjectType"), nextSymId c.idgen, getCurrOwner(c), s.info) + obj.flags.incl sfGeneratedType let symNode = newSymNode(obj) obj.ast = a.shallowCopy case a[0].kind @@ -1405,8 +1445,8 @@ proc typeSectionRightSidePass(c: PContext, n: PNode) = obj.ast[2] = a[2][0] if sfPure in s.flags: obj.flags.incl sfPure - obj.typ = st.lastSon - st.lastSon.sym = obj + obj.typ = objTy + objTy.sym = obj proc checkForMetaFields(c: PContext; n: PNode) = proc checkMeta(c: PContext; n: PNode; t: PType) = @@ -1443,7 +1483,7 @@ proc typeSectionFinalPass(c: PContext, n: PNode) = let name = typeSectionTypeName(c, a[0]) var s = name.sym # check the style here after the pragmas have been processed: - styleCheckDef(c.config, s) + styleCheckDef(c, s) # compute the type's size and check for illegal recursions: if a[1].kind == nkEmpty: var x = a[2] @@ -1676,7 +1716,7 @@ proc semInferredLambda(c: PContext, pt: TIdTable, n: PNode): PNode {.nosinks.} = addParams(c, params, skProc) pushProcCon(c, s) addResult(c, n, n.typ[0], skProc) - s.ast[bodyPos] = hloBody(c, semProcBody(c, n[bodyPos])) + s.ast[bodyPos] = hloBody(c, semProcBody(c, n[bodyPos], n.typ[0])) trackProc(c, s, s.ast[bodyPos]) popProcCon(c) popOwner(c) @@ -2041,7 +2081,7 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind, ("'" & proto.name.s & "' from " & c.config$proto.info & " '" & s.name.s & "' from " & c.config$s.info)) - styleCheckDef(c.config, s) + styleCheckDef(c, s) if hasProto: onDefResolveForward(n[namePos].info, proto) else: @@ -2098,7 +2138,7 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind, # allowed, everything else, including a nullary generic is an error. pushProcCon(c, s) addResult(c, n, s.typ[0], skProc) - s.ast[bodyPos] = hloBody(c, semProcBody(c, n[bodyPos])) + s.ast[bodyPos] = hloBody(c, semProcBody(c, n[bodyPos], s.typ[0])) trackProc(c, s, s.ast[bodyPos]) popProcCon(c) elif efOperand notin flags: @@ -2111,14 +2151,21 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind, if s.kind notin {skMacro, skTemplate} and s.magic == mNone: paramsTypeCheck(c, s.typ) maybeAddResult(c, s, n) + let resultType = + if s.kind == skMacro: + sysTypeFromName(c.graph, n.info, "NimNode") + elif not isInlineIterator(s.typ): + s.typ[0] + else: + nil # semantic checking also needed with importc in case used in VM - s.ast[bodyPos] = hloBody(c, semProcBody(c, n[bodyPos])) + s.ast[bodyPos] = hloBody(c, semProcBody(c, n[bodyPos], resultType)) # unfortunately we cannot skip this step when in 'system.compiles' # context as it may even be evaluated in 'system.compiles': trackProc(c, s, s.ast[bodyPos]) else: if (s.typ[0] != nil and s.kind != skIterator): - addDecl(c, newSym(skUnknown, getIdent(c.cache, "result"), nextSymId c.idgen, nil, n.info)) + addDecl(c, newSym(skUnknown, getIdent(c.cache, "result"), nextSymId c.idgen, s, n.info)) openScope(c) n[bodyPos] = semGenericStmt(c, n[bodyPos]) @@ -2289,7 +2336,12 @@ proc setLine(n: PNode, info: TLineInfo) = for i in 0.. 0: @@ -250,8 +250,14 @@ proc semTemplSymbol(c: PContext, n: PNode, s: PSym; isField: bool): PNode = of skUnknown: # Introduced in this pass! Leave it as an identifier. result = n - of OverloadableSyms-{skEnumField}: + of OverloadableSyms-{skTemplate,skMacro}: result = symChoice(c, n, s, scOpen, isField) + of skTemplate, skMacro: + result = symChoice(c, n, s, scOpen, isField) + if result.kind == nkSym: + # template/macro symbols might need to be semchecked again + # prepareOperand etc don't do this without setting the type to nil + result.typ = nil of skGenericParam: if isField and sfGenSym in s.flags: result = n else: result = newSymNodeTypeDesc(s, c.idgen, n.info) @@ -261,18 +267,14 @@ proc semTemplSymbol(c: PContext, n: PNode, s: PSym; isField: bool): PNode = if isField and sfGenSym in s.flags: result = n else: result = newSymNodeTypeDesc(s, c.idgen, n.info) else: - if s.kind == skEnumField and overloadableEnums in c.features: - result = symChoice(c, n, s, scOpen, isField) - elif isField and sfGenSym in s.flags: - result = n - else: - result = newSymNode(s, n.info) + if isField and sfGenSym in s.flags: result = n + else: result = newSymNode(s, n.info) # Issue #12832 when defined(nimsuggest): suggestSym(c.graph, n.info, s, c.graph.usageSym, false) # field access (dot expr) will be handled by builtinFieldAccess - if not isField and {optStyleHint, optStyleError} * c.config.globalOptions != {}: - styleCheckUse(c.config, n.info, s) + if not isField: + styleCheckUse(c, n.info, s) proc semRoutineInTemplName(c: var TemplCtx, n: PNode): PNode = result = n @@ -297,7 +299,7 @@ proc semRoutineInTemplBody(c: var TemplCtx, n: PNode, k: TSymKind): PNode = var s = newGenSym(k, ident, c) s.ast = n addPrelimDecl(c.c, s) - styleCheckDef(c.c.config, n.info, s) + styleCheckDef(c.c, n.info, s) onDef(n.info, s) n[namePos] = newSymNode(s, n[namePos].info) else: @@ -431,7 +433,7 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode = # labels are always 'gensym'ed: let s = newGenSym(skLabel, n[0], c) addPrelimDecl(c.c, s) - styleCheckDef(c.c.config, s) + styleCheckDef(c.c, s) onDef(n[0].info, s) n[0] = newSymNode(s, n[0].info) n[1] = semTemplBody(c, n[1]) @@ -624,7 +626,7 @@ proc semTemplateDef(c: PContext, n: PNode): PNode = s.owner.name.s == "vm" and s.name.s == "stackTrace": incl(s.flags, sfCallsite) - styleCheckDef(c.config, s) + styleCheckDef(c, s) onDef(n[namePos].info, s) # check parameter list: #s.scope = c.currentScope @@ -691,6 +693,9 @@ proc semTemplateDef(c: PContext, n: PNode): PNode = if proto == nil: addInterfaceOverloadableSymAt(c, c.currentScope, s) elif not comesFromShadowscope: + if {sfTemplateRedefinition, sfGenSym} * s.flags == {}: + #wrongRedefinition(c, n.info, proto.name.s, proto.info) + message(c.config, n.info, warnTemplateRedefinition, s.name.s) symTabReplace(c.currentScope.symbols, proto, s) if n[patternPos].kind != nkEmpty: c.patterns.add(s) diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index b4f385fe615f..0a823f20ad6d 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -16,8 +16,8 @@ const errIntLiteralExpected = "integer literal expected" errWrongNumberOfVariables = "wrong number of variables" errInvalidOrderInEnumX = "invalid order in enum '$1'" - errOrdinalTypeExpected = "ordinal type expected" - errSetTooBig = "set is too large" + errOrdinalTypeExpected = "ordinal type expected; given: $1" + errSetTooBig = "set is too large; use `std/sets` for ordinal types with more than 2^16 elements" errBaseTypeMustBeOrdinal = "base type of a set must be an ordinal" errInheritanceOnlyWithNonFinalObjects = "inheritance only works with non-final objects" errXExpectsOneTypeParam = "'$1' expects one type parameter" @@ -95,7 +95,7 @@ proc semEnum(c: PContext, n: PNode, prev: PType): PType = strVal = v[1] # second tuple part is the string value if skipTypes(strVal.typ, abstractInst).kind in {tyString, tyCstring}: if not isOrdinalType(v[0].typ, allowEnumWithHoles=true): - localError(c.config, v[0].info, errOrdinalTypeExpected & "; given: " & typeToString(v[0].typ, preferDesc)) + localError(c.config, v[0].info, errOrdinalTypeExpected % typeToString(v[0].typ, preferDesc)) x = toInt64(getOrdValue(v[0])) # first tuple part is the ordinal n[i][1][0] = newIntTypeNode(x, getSysType(c.graph, unknownLineInfo, tyInt)) else: @@ -107,7 +107,7 @@ proc semEnum(c: PContext, n: PNode, prev: PType): PType = x = counter else: if not isOrdinalType(v.typ, allowEnumWithHoles=true): - localError(c.config, v.info, errOrdinalTypeExpected & "; given: " & typeToString(v.typ, preferDesc)) + localError(c.config, v.info, errOrdinalTypeExpected % typeToString(v.typ, preferDesc)) x = toInt64(getOrdValue(v)) n[i][1] = newIntTypeNode(x, getSysType(c.graph, unknownLineInfo, tyInt)) if i != 1: @@ -139,14 +139,11 @@ proc semEnum(c: PContext, n: PNode, prev: PType): PType = e.flags.incl {sfUsed, sfExported} result.n.add symNode - styleCheckDef(c.config, e) + styleCheckDef(c, e) onDef(e.info, e) if sfGenSym notin e.flags: if not isPure: - if overloadableEnums in c.features: - addInterfaceOverloadableSymAt(c, c.currentScope, e) - else: - addInterfaceDecl(c, e) + addInterfaceOverloadableSymAt(c, c.currentScope, e) else: declarePureEnumField(c, e) if isPure and (let conflict = strTableInclReportConflict(symbols, e); conflict != nil): @@ -166,7 +163,7 @@ proc semSet(c: PContext, n: PNode, prev: PType): PType = if base.kind in {tyGenericInst, tyAlias, tySink}: base = lastSon(base) if base.kind notin {tyGenericParam, tyGenericInvocation}: if not isOrdinalType(base, allowEnumWithHoles = true): - localError(c.config, n.info, errOrdinalTypeExpected) + localError(c.config, n.info, errOrdinalTypeExpected % typeToString(base, preferDesc)) elif lengthOrd(c.config, base) > MaxSetElements: localError(c.config, n.info, errSetTooBig) else: @@ -212,10 +209,34 @@ proc semVarOutType(c: PContext, n: PNode, prev: PType; kind: TTypeKind): PType = else: result = newConstraint(c, kind) +proc isRecursiveType(t: PType, cycleDetector: var IntSet): bool = + if t == nil: + return false + if cycleDetector.containsOrIncl(t.id): + return true + case t.kind + of tyAlias, tyGenericInst, tyDistinct: + return isRecursiveType(t.lastSon, cycleDetector) + else: + return false + +proc isRecursiveType*(t: PType): bool = + # handle simple recusive types before typeFinalPass + var cycleDetector = initIntSet() + isRecursiveType(t, cycleDetector) + +proc addSonSkipIntLitChecked(c: PContext; father, son: PType; it: PNode, id: IdGenerator) = + let s = son.skipIntLit(id) + father.sons.add(s) + if isRecursiveType(s): + localError(c.config, it.info, "illegal recursion in type '" & typeToString(s) & "'") + else: + propagateToOwner(father, s) + proc semDistinct(c: PContext, n: PNode, prev: PType): PType = if n.len == 0: return newConstraint(c, tyDistinct) result = newOrPrevType(tyDistinct, prev, c) - addSonSkipIntLit(result, semTypeNode(c, n[0], nil), c.idgen) + addSonSkipIntLitChecked(c, result, semTypeNode(c, n[0], nil), n[0], c.idgen) if n.len > 1: result.n = n[1] proc semRangeAux(c: PContext, n: PNode, prev: PType): PType = @@ -310,12 +331,12 @@ proc semArrayIndex(c: PContext, n: PNode): PType = return semArrayIndex(c, e.sym.ast) if not isOrdinalType(e.typ.lastSon): let info = if n.safeLen > 1: n[1].info else: n.info - localError(c.config, info, errOrdinalTypeExpected) + localError(c.config, info, errOrdinalTypeExpected % typeToString(e.typ, preferDesc)) result = makeRangeWithStaticExpr(c, e) if c.inGenericContext > 0: result.flags.incl tfUnresolved elif e.kind in (nkCallKinds + {nkBracketExpr}) and hasUnresolvedArgs(c, e): if not isOrdinalType(e.typ.skipTypes({tyStatic, tyAlias, tyGenericInst, tySink})): - localError(c.config, n[1].info, errOrdinalTypeExpected) + localError(c.config, n[1].info, errOrdinalTypeExpected % typeToString(e.typ, preferDesc)) # This is an int returning call, depending on an # yet unknown generic param (see tgenericshardcases). # We are going to construct a range type that will be @@ -344,7 +365,7 @@ proc semArray(c: PContext, n: PNode, prev: PType): PType = if indxB.skipTypes({tyRange}).kind in {tyUInt, tyUInt64}: discard elif not isOrdinalType(indxB): - localError(c.config, n[1].info, errOrdinalTypeExpected) + localError(c.config, n[1].info, errOrdinalTypeExpected % typeToString(indxB, preferDesc)) elif enumHasHoles(indxB): localError(c.config, n[1].info, "enum '$1' has holes" % typeToString(indxB.skipTypes({tyRange}))) @@ -374,7 +395,7 @@ proc semOrdinal(c: PContext, n: PNode, prev: PType): PType = var base = semTypeNode(c, n[1], nil) if base.kind != tyGenericParam: if not isOrdinalType(base): - localError(c.config, n[1].info, errOrdinalTypeExpected) + localError(c.config, n[1].info, errOrdinalTypeExpected % typeToString(base, preferDesc)) addSonSkipIntLit(result, base, c.idgen) else: localError(c.config, n.info, errXExpectsOneTypeParam % "ordinal") @@ -447,7 +468,8 @@ proc semAnonTuple(c: PContext, n: PNode, prev: PType): PType = localError(c.config, n.info, errTypeExpected) result = newOrPrevType(tyTuple, prev, c) for it in n: - addSonSkipIntLit(result, semTypeNode(c, it, nil), c.idgen) + let t = semTypeNode(c, it, nil) + addSonSkipIntLitChecked(c, result, t, it, c.idgen) proc semTuple(c: PContext, n: PNode, prev: PType): PType = var typ: PType @@ -476,7 +498,7 @@ proc semTuple(c: PContext, n: PNode, prev: PType): PType = else: result.n.add newSymNode(field) addSonSkipIntLit(result, typ, c.idgen) - styleCheckDef(c.config, a[j].info, field) + styleCheckDef(c, a[j].info, field) onDef(field.info, field) if result.n.len == 0: result.n = nil if isTupleRecursive(result): @@ -578,7 +600,9 @@ proc semCaseBranch(c: PContext, t, branch: PNode, branchIndex: int, checkMinSonsLen(t, 1, c.config) var tmp = fitNode(c, t[0].typ, r, r.info) # the call to fitNode may introduce a call to a converter - if tmp.kind in {nkHiddenCallConv}: tmp = semConstExpr(c, tmp) + if tmp.kind == nkHiddenCallConv or + (tmp.kind == nkHiddenStdConv and t[0].typ.kind == tyCstring): + tmp = semConstExpr(c, tmp) branch[i] = skipConv(tmp) inc(covered) else: @@ -800,7 +824,7 @@ proc semRecordNodeAux(c: PContext, n: PNode, check: var IntSet, pos: var int, f.options = c.config.options if fieldOwner != nil and {sfImportc, sfExportc} * fieldOwner.flags != {} and - not hasCaseFields and f.loc.r == nil: + not hasCaseFields and f.loc.r == "": f.loc.r = rope(f.name.s) f.flags.incl {sfImportc, sfExportc} * fieldOwner.flags inc(pos) @@ -808,7 +832,7 @@ proc semRecordNodeAux(c: PContext, n: PNode, check: var IntSet, pos: var int, localError(c.config, info, "attempt to redefine: '" & f.name.s & "'") if a.kind == nkEmpty: father.add newSymNode(f) else: a.add newSymNode(f) - styleCheckDef(c.config, f) + styleCheckDef(c, f) onDef(f.info, f) if a.kind != nkEmpty: father.add a of nkSym: @@ -1315,7 +1339,7 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode, result.n.add newSymNode(arg) rawAddSon(result, finalType) addParamOrResult(c, arg, kind) - styleCheckDef(c.config, a[j].info, arg) + styleCheckDef(c, a[j].info, arg) onDef(a[j].info, arg) if {optNimV1Emulation, optNimV12Emulation} * c.config.globalOptions == {}: a[j] = newSymNode(arg) diff --git a/compiler/semtypinst.nim b/compiler/semtypinst.nim index 504b83b4cc2c..945667a81987 100644 --- a/compiler/semtypinst.nim +++ b/compiler/semtypinst.nim @@ -499,17 +499,20 @@ proc propagateFieldFlags(t: PType, n: PNode) = proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType = template bailout = - if cl.recursionLimit > 100: - # bail out, see bug #2509. But note this caching is in general wrong, - # look at this example where TwoVectors should not share the generic - # instantiations (bug #3112): - - # type - # Vector[N: static[int]] = array[N, float64] - # TwoVectors[Na, Nb: static[int]] = (Vector[Na], Vector[Nb]) - result = PType(idTableGet(cl.localCache, t)) - if result != nil: return result - inc cl.recursionLimit + if t.sym != nil and sfGeneratedType in t.sym.flags: + # Only consider the recursion limit if the symbol is a type with generic + # parameters that have not been explicitly supplied, typechecking should + # terminate when generic parameters are explicitly supplied. + if cl.recursionLimit > 100: + # bail out, see bug #2509. But note this caching is in general wrong, + # look at this example where TwoVectors should not share the generic + # instantiations (bug #3112): + # type + # Vector[N: static[int]] = array[N, float64] + # TwoVectors[Na, Nb: static[int]] = (Vector[Na], Vector[Nb]) + result = PType(idTableGet(cl.localCache, t)) + if result != nil: return result + inc cl.recursionLimit result = t if t == nil: return diff --git a/compiler/sighashes.nim b/compiler/sighashes.nim index 1835d9d0f705..3dfd71315fb3 100644 --- a/compiler/sighashes.nim +++ b/compiler/sighashes.nim @@ -21,8 +21,7 @@ proc `&=`(c: var MD5Context, s: string) = md5Update(c, s, s.len) proc `&=`(c: var MD5Context, ch: char) = # XXX suspicious code here; relies on ch being zero terminated? md5Update(c, unsafeAddr ch, 1) -proc `&=`(c: var MD5Context, r: Rope) = - for l in leaves(r): md5Update(c, l.cstring, l.len) + proc `&=`(c: var MD5Context, i: BiggestInt) = md5Update(c, cast[cstring](unsafeAddr i), sizeof(i)) proc `&=`(c: var MD5Context, f: BiggestFloat) = @@ -150,7 +149,7 @@ proc hashType(c: var MD5Context, t: PType; flags: set[ConsiderFlag]) = # is actually safe without an infinite recursion check: if t.sym != nil: if {sfCompilerProc} * t.sym.flags != {}: - doAssert t.sym.loc.r != nil + doAssert t.sym.loc.r != "" # The user has set a specific name for this type c &= t.sym.loc.r elif CoOwnerSig in flags: @@ -334,7 +333,7 @@ proc hashVarSymBody(graph: ModuleGraph, c: var MD5Context, s: PSym) = c &= hashNonProc(s) # this one works for let and const but not for var. True variables can change value # later on. it is user resposibility to hash his global state if required - if s.ast != nil and s.ast.kind == nkIdentDefs: + if s.ast != nil and s.ast.kind in {nkIdentDefs, nkConstDef}: hashBodyTree(graph, c, s.ast[^1]) else: hashBodyTree(graph, c, s.ast) diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index f195c4e45585..290b9c8db0b4 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -550,11 +550,10 @@ proc inconsistentVarTypes(f, a: PType): bool {.inline.} = proc procParamTypeRel(c: var TCandidate, f, a: PType): TTypeRelation = ## For example we have: - ## - ## .. code-block:: nim + ## ``` ## proc myMap[T,S](sIn: seq[T], f: proc(x: T): S): seq[S] = ... ## proc innerProc[Q,W](q: Q): W = ... - ## + ## ``` ## And we want to match: myMap(@[1,2,3], innerProc) ## This proc (procParamTypeRel) will do the following steps in ## three different calls: @@ -1305,8 +1304,6 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, if sameDistinctTypes(f, a): result = isEqual #elif f.base.kind == tyAnything: result = isGeneric # issue 4435 elif c.coerceDistincts: result = typeRel(c, f.base, a, flags) - elif a.kind == tyNil and f.base.kind in NilableTypes: - result = f.allowsNil # XXX remove this typing rule, it is not in the spec elif c.coerceDistincts: result = typeRel(c, f.base, a, flags) of tySet: if a.kind == tySet: diff --git a/compiler/sizealignoffsetimpl.nim b/compiler/sizealignoffsetimpl.nim index c2e97aa531ed..009acf6eb27f 100644 --- a/compiler/sizealignoffsetimpl.nim +++ b/compiler/sizealignoffsetimpl.nim @@ -454,9 +454,9 @@ proc computeSizeAlign(conf: ConfigRef; typ: PType) = setSize typ, 1 of tyInt16, tyUInt16: setSize typ, 2 - of tyInt32, tyUInt32: + of tyInt32, tyUInt32, tyFloat32: setSize typ, 4 - of tyInt64, tyUInt64: + of tyInt64, tyUInt64, tyFloat64, tyFloat: setSize typ, 8 else: typ.size = szUnknownSize diff --git a/compiler/suggest.nim b/compiler/suggest.nim index 84c94d7933f8..9d40897071fe 100644 --- a/compiler/suggest.nim +++ b/compiler/suggest.nim @@ -117,7 +117,7 @@ proc getTokenLenFromSource(conf: ConfigRef; ident: string; info: TLineInfo): int elif sourceIdent != ident: result = 0 -proc symToSuggest(g: ModuleGraph; s: PSym, isLocal: bool, section: IdeCmd, info: TLineInfo; +proc symToSuggest*(g: ModuleGraph; s: PSym, isLocal: bool, section: IdeCmd, info: TLineInfo; quality: range[0..100]; prefix: PrefixMatch; inTypeContext: bool; scope: int; useSuppliedInfo = false): Suggest = @@ -157,19 +157,25 @@ proc symToSuggest(g: ModuleGraph; s: PSym, isLocal: bool, section: IdeCmd, info: result.forth = "" when defined(nimsuggest) and not defined(noDocgen) and not defined(leanCompiler): result.doc = extractDocComment(g, s) - let infox = - if useSuppliedInfo or section in {ideUse, ideHighlight, ideOutline}: - info - else: - s.info - result.filePath = toFullPath(g.config, infox) - result.line = toLinenumber(infox) - result.column = toColumn(infox) + if s.kind == skModule and s.ast.len != 0 and section != ideHighlight: + result.filePath = toFullPath(g.config, s.ast[0].info) + result.line = 1 + result.column = 0 + result.tokenLen = 0 + else: + let infox = + if useSuppliedInfo or section in {ideUse, ideHighlight, ideOutline}: + info + else: + s.info + result.filePath = toFullPath(g.config, infox) + result.line = toLinenumber(infox) + result.column = toColumn(infox) + result.tokenLen = if section != ideHighlight: + s.name.s.len + else: + getTokenLenFromSource(g.config, s.name.s, infox) result.version = g.config.suggestVersion - result.tokenLen = if section != ideHighlight: - s.name.s.len - else: - getTokenLenFromSource(g.config, s.name.s, infox) proc `$`*(suggest: Suggest): string = result = $suggest.section @@ -203,14 +209,14 @@ proc `$`*(suggest: Suggest): string = result.add(sep) when defined(nimsuggest) and not defined(noDocgen) and not defined(leanCompiler): result.add(suggest.doc.escape) - if suggest.version == 0: + if suggest.version in {0, 3}: result.add(sep) result.add($suggest.quality) if suggest.section == ideSug: result.add(sep) result.add($suggest.prefix) -proc suggestResult(conf: ConfigRef; s: Suggest) = +proc suggestResult*(conf: ConfigRef; s: Suggest) = if not isNil(conf.suggestionResultHook): conf.suggestionResultHook(s) else: @@ -424,7 +430,7 @@ proc suggestFieldAccess(c: PContext, n, field: PNode, outputs: var Suggestions) t = skipTypes(t[0], skipPtrs) elif typ.kind == tyTuple and typ.n != nil: suggestSymList(c, typ.n, field, n.info, outputs) - + suggestOperations(c, n, field, orig, outputs) if typ != orig: suggestOperations(c, n, field, typ, outputs) @@ -482,7 +488,7 @@ proc findDefinition(g: ModuleGraph; info: TLineInfo; s: PSym; usageSym: var PSym if s.isNil: return if isTracked(info, g.config.m.trackPos, s.name.s.len) or (s == usageSym and sfForward notin s.flags): suggestResult(g.config, symToSuggest(g, s, isLocal=false, ideDef, info, 100, PrefixMatch.None, false, 0, useSuppliedInfo = s == usageSym)) - if sfForward notin s.flags: + if sfForward notin s.flags and g.config.suggestVersion != 3: suggestQuit() else: usageSym = s @@ -497,6 +503,8 @@ proc suggestSym*(g: ModuleGraph; info: TLineInfo; s: PSym; usageSym: var PSym; i ## misnamed: should be 'symDeclared' let conf = g.config when defined(nimsuggest): + g.suggestSymbols.mgetOrPut(info.fileIndex, @[]).add SymInfoPair(sym: s, info: info) + if conf.suggestVersion == 0: if s.allUsages.len == 0: s.allUsages = @[info] @@ -527,16 +535,6 @@ proc suggestSym*(g: ModuleGraph; info: TLineInfo; s: PSym; usageSym: var PSym; i if parentFileIndex == conf.m.trackPos.fileIndex: suggestResult(conf, symToSuggest(g, s, isLocal=false, ideOutline, info, 100, PrefixMatch.None, false, 0)) -proc extractPragma(s: PSym): PNode = - if s.kind in routineKinds: - result = s.ast[pragmasPos] - elif s.kind in {skType, skVar, skLet}: - if s.ast != nil and s.ast.len > 0: - if s.ast[0].kind == nkPragmaExpr and s.ast[0].len > 1: - # s.ast = nkTypedef / nkPragmaExpr / [nkSym, nkPragma] - result = s.ast[0][1] - doAssert result == nil or result.kind == nkPragma - proc warnAboutDeprecated(conf: ConfigRef; info: TLineInfo; s: PSym) = var pragmaNode: PNode pragmaNode = if s.kind == skEnumField: extractPragma(s.owner) else: extractPragma(s) @@ -596,8 +594,7 @@ proc markUsed(c: PContext; info: TLineInfo; s: PSym) = if sfError in s.flags: userError(conf, info, s) when defined(nimsuggest): suggestSym(c.graph, info, s, c.graph.usageSym, false) - if {optStyleHint, optStyleError} * conf.globalOptions != {}: - styleCheckUse(conf, info, s) + styleCheckUse(c, info, s) markOwnerModuleAsUsed(c, s) proc safeSemExpr*(c: PContext, n: PNode): PNode = @@ -692,3 +689,16 @@ proc suggestSentinel*(c: PContext) = dec(c.compilesContextId) produceOutput(outputs, c.config) + +when defined(nimsuggest): + proc onDef(graph: ModuleGraph, s: PSym, info: TLineInfo) = + if graph.config.suggestVersion == 3 and info.exactEquals(s.info): + suggestSym(graph, info, s, graph.usageSym) + + template getPContext(): untyped = + when c is PContext: c + else: c.c + + template onDef*(info: TLineInfo; s: PSym) = + let c = getPContext() + onDef(c.graph, s, info) diff --git a/compiler/tccgen.nim b/compiler/tccgen.nim index ba553906a16e..83c891ca8b71 100644 --- a/compiler/tccgen.nim +++ b/compiler/tccgen.nim @@ -48,15 +48,15 @@ proc setupEnvironment = var tinycRoot = nimDir / tinyPrefix let libpath = nimDir / "lib" - addIncludePath(gTinyC, libpath) + addIncludePath(gTinyC, cstring(libpath)) when defined(windows): - addSysincludePath(gTinyC, tinycRoot / "tinyc/win32/include") - addSysincludePath(gTinyC, tinycRoot / "tinyc/include") + addSysincludePath(gTinyC, cstring(tinycRoot / "tinyc/win32/include")) + addSysincludePath(gTinyC, cstring(tinycRoot / "tinyc/include")) when defined(windows): defineSymbol(gTinyC, "_WIN32", nil) # we need Mingw's headers too: var gccbin = getConfigVar("gcc.path") % ["nim", tinycRoot] - addSysincludePath(gTinyC, gccbin /../ "include") + addSysincludePath(gTinyC, cstring(gccbin /../ "include")) #addFile(tinycRoot / r"tinyc\win32\wincrt1.o") addFile(tinycRoot / r"tinyc\win32\alloca86.o") addFile(tinycRoot / r"tinyc\win32\chkstk.o") diff --git a/compiler/transf.nim b/compiler/transf.nim index bdd7c680ccd5..7ab67873b4f9 100644 --- a/compiler/transf.nim +++ b/compiler/transf.nim @@ -184,10 +184,12 @@ proc transformVarSection(c: PTransf, v: PNode): PNode = if it.kind == nkCommentStmt: result[i] = it elif it.kind == nkIdentDefs: - if it[0].kind == nkSym: + var vn = it[0] + if vn.kind == nkPragmaExpr: vn = vn[0] + if vn.kind == nkSym: internalAssert(c.graph.config, it.len == 3) - let x = freshVar(c, it[0].sym) - idNodeTablePut(c.transCon.mapping, it[0].sym, x) + let x = freshVar(c, vn.sym) + idNodeTablePut(c.transCon.mapping, vn.sym, x) var defs = newTransNode(nkIdentDefs, it.info, 3) if importantComments(c.graph.config): # keep documentation information: @@ -806,6 +808,8 @@ proc getMergeOp(n: PNode): PSym = else: discard proc flattenTreeAux(d, a: PNode, op: PSym) = + ## Optimizes away the `&` calls in the children nodes and + ## lifts the leaf nodes to the same level as `op2`. let op2 = getMergeOp(a) if op2 != nil and (op2.id == op.id or op.magic != mNone and op2.magic == op.magic): @@ -898,6 +902,9 @@ proc transformExceptBranch(c: PTransf, n: PNode): PNode = result = transformSons(c, n) proc commonOptimizations*(g: ModuleGraph; idgen: IdGenerator; c: PSym, n: PNode): PNode = + ## Merges adjacent constant expressions of the children of the `&` call into + ## a single constant expression. It also inlines constant expressions which are not + ## complex. result = n for i in 0..= 0: c.s[vid].flags.incl viewDoesMutate - #[of immutableView: - if dest.kind == nkBracketExpr and dest[0].kind == nkHiddenDeref and - mutableParameter(dest[0][0]): - discard "remains a mutable location anyhow" + #[of immutableView: + if dest.kind == nkBracketExpr and dest[0].kind == nkHiddenDeref and + mutableParameter(dest[0][0]): + discard "remains a mutable location anyhow" + else: + localError(c.g.config, dest.info, "attempt to mutate a borrowed location from an immutable view") + ]# else: - localError(c.g.config, dest.info, "attempt to mutate a borrowed location from an immutable view") - ]# - of noView: discard "nothing to do" + discard "nothing to do" proc containsPointer(t: PType): bool = proc wrap(t: PType): bool {.nimcall.} = t.kind in {tyRef, tyPtr} @@ -765,6 +776,11 @@ proc traverse(c: var Partitions; n: PNode) = # mutate(graph) # connect(graph, cursorVar) for child in n: traverse(c, child) + + if n.kind == nkWhileStmt: + traverse(c, n[0]) + # variables in while condition has longer alive time than local variables + # in the while loop body else: for child in n: traverse(c, child) @@ -851,7 +867,12 @@ proc computeLiveRanges(c: var Partitions; n: PNode) = # connect(graph, cursorVar) inc c.inLoop for child in n: computeLiveRanges(c, child) - inc c.inLoop + dec c.inLoop + + if n.kind == nkWhileStmt: + computeLiveRanges(c, n[0]) + # variables in while condition has longer alive time than local variables + # in the while loop body of nkElifBranch, nkElifExpr, nkElse, nkOfBranch: inc c.inConditional for child in n: computeLiveRanges(c, child) diff --git a/compiler/vm.nim b/compiler/vm.nim index fa1c71c85f4e..1717fe1194fe 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -18,6 +18,9 @@ import gorgeimpl, lineinfos, btrees, macrocacheimpl, modulegraphs, sighashes, int128, vmprofiler +when defined(nimPreviewSlimSystem): + import std/formatfloat + import ast except getstr from semfold import leValueConv, ordinalValToString from evaltempl import evalTemplate @@ -114,18 +117,22 @@ template decodeBx(k: untyped) {.dirty.} = let rbx = instr.regBx - wordExcess ensureKind(k) -template move(a, b: untyped) {.dirty.} = system.shallowCopy(a, b) -# XXX fix minor 'shallowCopy' overloading bug in compiler +template move(a, b: untyped) {.dirty.} = + when defined(gcArc) or defined(gcOrc): + a = move b + else: + system.shallowCopy(a, b) + # XXX fix minor 'shallowCopy' overloading bug in compiler proc derefPtrToReg(address: BiggestInt, typ: PType, r: var TFullReg, isAssign: bool): bool = # nim bug: `isAssign: static bool` doesn't work, giving odd compiler error - template fun(field, T, rkind) = + template fun(field, typ, rkind) = if isAssign: - cast[ptr T](address)[] = T(r.field) + cast[ptr typ](address)[] = typ(r.field) else: r.ensureKind(rkind) - let val = cast[ptr T](address)[] - when T is SomeInteger | char: + let val = cast[ptr typ](address)[] + when typ is SomeInteger | char: r.field = BiggestInt(val) else: r.field = val @@ -523,8 +530,7 @@ when not defined(nimHasSinkInference): template takeAddress(reg, source) = reg.nodeAddr = addr source - when defined(gcDestructors): - GC_ref source + GC_ref source proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = var pc = start @@ -642,7 +648,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = of opcNodeToReg: let ra = instr.regA let rb = instr.regB - # opcDeref might already have loaded it into a register. XXX Let's hope + # opcLdDeref might already have loaded it into a register. XXX Let's hope # this is still correct this way: if regs[rb].kind != rkNode: regs[ra] = regs[rb] @@ -1011,6 +1017,12 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = decodeBC(rkInt) template getTyp(n): untyped = n.typ.skipTypes(abstractInst) + template skipRegisterAddr(n: TFullReg): TFullReg = + var tmp = n + while tmp.kind == rkRegisterAddr: + tmp = tmp.regAddr[] + tmp + proc ptrEquality(n1: ptr PNode, n2: PNode): bool = ## true if n2.intVal represents a ptr equal to n1 let p1 = cast[int](n1) @@ -1024,16 +1036,19 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = return t2.kind in PtrLikeKinds and n2.intVal == p1 else: return false - if regs[rb].kind == rkNodeAddr: - if regs[rc].kind == rkNodeAddr: - ret = regs[rb].nodeAddr == regs[rc].nodeAddr + let rbReg = skipRegisterAddr(regs[rb]) + let rcReg = skipRegisterAddr(regs[rc]) + + if rbReg.kind == rkNodeAddr: + if rcReg.kind == rkNodeAddr: + ret = rbReg.nodeAddr == rcReg.nodeAddr else: - ret = ptrEquality(regs[rb].nodeAddr, regs[rc].node) - elif regs[rc].kind == rkNodeAddr: - ret = ptrEquality(regs[rc].nodeAddr, regs[rb].node) + ret = ptrEquality(rbReg.nodeAddr, rcReg.node) + elif rcReg.kind == rkNodeAddr: + ret = ptrEquality(rcReg.nodeAddr, rbReg.node) else: - let nb = regs[rb].node - let nc = regs[rc].node + let nb = rbReg.node + let nc = rcReg.node if nb.kind != nc.kind: discard elif (nb == nc) or (nb.kind == nkNilLit): ret = true # intentional elif nb.kind in {nkSym, nkTupleConstr, nkClosure} and nb.typ != nil and nb.typ.kind == tyProc and sameConstant(nb, nc): @@ -1185,14 +1200,6 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = of opcContainsSet: decodeBC(rkInt) regs[ra].intVal = ord(inSet(regs[rb].node, regs[rc].regToNode)) - of opcSubStr: - decodeBC(rkNode) - inc pc - assert c.code[pc].opcode == opcSubStr - let rd = c.code[pc].regA - createStr regs[ra] - regs[ra].node.strVal = substr(regs[rb].node.strVal, - regs[rc].intVal.int, regs[rd].intVal.int) of opcParseFloat: decodeBC(rkInt) inc pc @@ -2260,7 +2267,6 @@ proc setupMacroParam(x: PNode, typ: PType): TFullReg = else: var n = x if n.kind in {nkHiddenSubConv, nkHiddenStdConv}: n = n[1] - n = n.canonValue n.flags.incl nfIsRef n.typ = x.typ result = TFullReg(kind: rkNode, node: n) diff --git a/compiler/vmdef.nim b/compiler/vmdef.nim index ecdbeff89b78..c653501caaf8 100644 --- a/compiler/vmdef.nim +++ b/compiler/vmdef.nim @@ -102,7 +102,7 @@ type opcMulSet, opcPlusSet, opcMinusSet, opcConcatStr, opcContainsSet, opcRepr, opcSetLenStr, opcSetLenSeq, opcIsNil, opcOf, opcIs, - opcSubStr, opcParseFloat, opcConv, opcCast, + opcParseFloat, opcConv, opcCast, opcQuit, opcInvalidField, opcNarrowS, opcNarrowU, opcSignExtend, @@ -306,7 +306,7 @@ proc registerCallback*(c: PCtx; name: string; callback: VmCallback): int {.disca const firstABxInstr* = opcTJmp largeInstrs* = { # instructions which use 2 int32s instead of 1: - opcSubStr, opcConv, opcCast, opcNewSeq, opcOf + opcConv, opcCast, opcNewSeq, opcOf } slotSomeTemp* = slotTempUnknown relativeJumps* = {opcTJmp, opcFJmp, opcJmp, opcJmpBack} diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim index dadcc3865353..a0bf6af566ed 100644 --- a/compiler/vmgen.nim +++ b/compiler/vmgen.nim @@ -15,11 +15,10 @@ # this doesn't matter. However it matters for strings and other complex # types that use the 'node' field; the reason is that slots are # re-used in a register based VM. Example: -# -#.. code-block:: nim -# let s = a & b # no matter what, create fresh node -# s = a & b # no matter what, keep the node -# +# ```nim +# let s = a & b # no matter what, create fresh node +# s = a & b # no matter what, keep the node +# ``` # Also *stores* into non-temporary memory need to perform deep copies: # a.b = x.y # We used to generate opcAsgn for the *load* of 'x.y' but this is clearly @@ -439,14 +438,11 @@ proc genAndOr(c: PCtx; n: PNode; opc: TOpcode; dest: var TDest) = c.gABC(n, opcAsgnInt, dest, tmp) freeTemp(c, tmp) -proc canonValue*(n: PNode): PNode = - result = n - proc rawGenLiteral(c: PCtx; n: PNode): int = result = c.constants.len #assert(n.kind != nkCall) n.flags.incl nfAllConst - c.constants.add n.canonValue + c.constants.add n internalAssert c.config, result < regBxMax proc sameConstant*(a, b: PNode): bool = @@ -1029,7 +1025,7 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) = c.genAsgnPatch(n[1], d) c.freeTemp(d) of mOrd, mChr, mArrToSeq, mUnown: c.gen(n[1], dest) - of mIsolate, mFinished: + of generatedMagics: genCall(c, n, dest) of mNew, mNewFinalize: unused(c, n, dest) @@ -1385,9 +1381,6 @@ proc unneededIndirection(n: PNode): bool = n.typ.skipTypes(abstractInstOwned-{tyTypeDesc}).kind == tyRef proc canElimAddr(n: PNode): PNode = - if n[0].typ.skipTypes(abstractInst).kind in {tyObject, tyTuple, tyArray}: - # objects are reference types in the VM - return n[0] case n[0].kind of nkObjUpConv, nkObjDownConv, nkChckRange, nkChckRangeF, nkChckRange64: var m = n[0][0] @@ -1857,7 +1850,7 @@ proc genVarSection(c: PCtx; n: PNode) = else: let sa = getNullValue(s.typ, a.info, c.config) #if s.ast.isNil: getNullValue(s.typ, a.info) - #else: canonValue(s.ast) + #else: s.ast assert sa.kind != nkCall c.globals.add(sa) s.position = c.globals.len @@ -2007,7 +2000,7 @@ proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) = elif importcCond(c, s): c.importcSym(n.info, s) genLit(c, n, dest) of skConst: - let constVal = if s.ast != nil: s.ast else: s.typ.n + let constVal = if s.astdef != nil: s.astdef else: s.typ.n gen(c, constVal, dest) of skEnumField: # we never reach this case - as of the time of this comment, diff --git a/compiler/vmmarshal.nim b/compiler/vmmarshal.nim index 83c441283ee1..e44bc0bea3b9 100644 --- a/compiler/vmmarshal.nim +++ b/compiler/vmmarshal.nim @@ -13,7 +13,7 @@ import streams, json, intsets, tables, ast, astalgo, idents, types, msgs, options, lineinfos when defined(nimPreviewSlimSystem): - import std/assertions + import std/[assertions, formatfloat] proc ptrToInt(x: PNode): int {.inline.} = result = cast[int](x) # don't skip alignment diff --git a/compiler/vmops.nim b/compiler/vmops.nim index 640826cc8809..fef76940e433 100644 --- a/compiler/vmops.nim +++ b/compiler/vmops.nim @@ -28,12 +28,12 @@ from std/os import getEnv, existsEnv, delEnv, putEnv, envPairs, from std/times import cpuTime from std/hashes import hash from std/osproc import nil -from system/formatfloat import addFloatRoundtrip, addFloatSprintf when defined(nimPreviewSlimSystem): - import std/[syncio, assertions] - + import std/syncio +else: + from std/formatfloat import addFloatRoundtrip, addFloatSprintf # There are some useful procs in vmconv. import vmconv, vmmarshal @@ -56,13 +56,13 @@ template ioop(op) {.dirty.} = template macrosop(op) {.dirty.} = registerCallback(c, "stdlib.macros." & astToStr(op), `op Wrapper`) -template wrap1f_math(op) {.dirty.} = +template wrap1fMath(op) {.dirty.} = proc `op Wrapper`(a: VmArgs) {.nimcall.} = doAssert a.numArgs == 1 setResult(a, op(getFloat(a, 0))) mathop op -template wrap2f_math(op) {.dirty.} = +template wrap2fMath(op) {.dirty.} = proc `op Wrapper`(a: VmArgs) {.nimcall.} = setResult(a, op(getFloat(a, 0), getFloat(a, 1))) mathop op @@ -172,40 +172,40 @@ proc registerAdditionalOps*(c: PCtx) = proc getProjectPathWrapper(a: VmArgs) = setResult a, c.config.projectPath.string - wrap1f_math(sqrt) - wrap1f_math(cbrt) - wrap1f_math(ln) - wrap1f_math(log10) - wrap1f_math(log2) - wrap1f_math(exp) - wrap1f_math(arccos) - wrap1f_math(arcsin) - wrap1f_math(arctan) - wrap1f_math(arcsinh) - wrap1f_math(arccosh) - wrap1f_math(arctanh) - wrap2f_math(arctan2) - wrap1f_math(cos) - wrap1f_math(cosh) - wrap2f_math(hypot) - wrap1f_math(sinh) - wrap1f_math(sin) - wrap1f_math(tan) - wrap1f_math(tanh) - wrap2f_math(pow) - wrap1f_math(trunc) - wrap1f_math(floor) - wrap1f_math(ceil) - wrap1f_math(erf) - wrap1f_math(erfc) - wrap1f_math(gamma) - wrap1f_math(lgamma) + wrap1fMath(sqrt) + wrap1fMath(cbrt) + wrap1fMath(ln) + wrap1fMath(log10) + wrap1fMath(log2) + wrap1fMath(exp) + wrap1fMath(arccos) + wrap1fMath(arcsin) + wrap1fMath(arctan) + wrap1fMath(arcsinh) + wrap1fMath(arccosh) + wrap1fMath(arctanh) + wrap2fMath(arctan2) + wrap1fMath(cos) + wrap1fMath(cosh) + wrap2fMath(hypot) + wrap1fMath(sinh) + wrap1fMath(sin) + wrap1fMath(tan) + wrap1fMath(tanh) + wrap2fMath(pow) + wrap1fMath(trunc) + wrap1fMath(floor) + wrap1fMath(ceil) + wrap1fMath(erf) + wrap1fMath(erfc) + wrap1fMath(gamma) + wrap1fMath(lgamma) when declared(copySign): - wrap2f_math(copySign) + wrap2fMath(copySign) when declared(signbit): - wrap1f_math(signbit) + wrap1fMath(signbit) registerCallback c, "stdlib.math.round", proc (a: VmArgs) {.nimcall.} = let n = a.numArgs @@ -324,6 +324,8 @@ proc registerAdditionalOps*(c: PCtx) = getEffectList(c, a, exceptionEffects) registerCallback c, "stdlib.effecttraits.getTagsListImpl", proc (a: VmArgs) = getEffectList(c, a, tagEffects) + registerCallback c, "stdlib.effecttraits.getForbidsListImpl", proc (a: VmArgs) = + getEffectList(c, a, forbiddenEffects) registerCallback c, "stdlib.effecttraits.isGcSafeImpl", proc (a: VmArgs) = let fn = getNode(a, 0) @@ -364,21 +366,5 @@ proc registerAdditionalOps*(c: PCtx) = let typ = a.getNode(0).typ let p = a.getReg(1) var res: string - - var node: PNode - case p.kind - of rkNone: - node = newNode(nkEmpty) - of rkInt: - node = newIntNode(nkIntLit, p.intVal) - of rkFloat: - node = newFloatNode(nkFloatLit, p.floatVal) - of rkNode: - node = p.node - of rkRegisterAddr: - node = p.regAddr.node - of rkNodeAddr: - node = p.nodeAddr[] - - storeAny(res, typ, node, c.config) + storeAny(res, typ, regToNode(p[]), c.config) setResult(a, res) diff --git a/compiler/wordrecg.nim b/compiler/wordrecg.nim index c819f4306388..d33982b0fedf 100644 --- a/compiler/wordrecg.nim +++ b/compiler/wordrecg.nim @@ -71,7 +71,7 @@ type wSinkInference = "sinkInference", wWarnings = "warnings", wHints = "hints", wOptimization = "optimization", wRaises = "raises", wWrites = "writes", wReads = "reads", wSize = "size", wEffects = "effects", wTags = "tags", - wRequires = "requires", wEnsures = "ensures", wInvariant = "invariant", + wForbids = "forbids", wRequires = "requires", wEnsures = "ensures", wInvariant = "invariant", wAssume = "assume", wAssert = "assert", wDeadCodeElimUnused = "deadCodeElim", # deprecated, dead code elim always happens wSafecode = "safecode", wPackage = "package", wNoForward = "noforward", wReorder = "reorder", @@ -80,32 +80,33 @@ type wLocalPassc = "localPassC", wBorrow = "borrow", wDiscardable = "discardable", wFieldChecks = "fieldChecks", wSubsChar = "subschar", wAcyclic = "acyclic", wShallow = "shallow", wUnroll = "unroll", wLinearScanEnd = "linearScanEnd", - wComputedGoto = "computedGoto", wExperimental = "experimental", + wComputedGoto = "computedGoto", wExperimental = "experimental", wDoctype = "doctype", wWrite = "write", wGensym = "gensym", wInject = "inject", wDirty = "dirty", wInheritable = "inheritable", wThreadVar = "threadvar", wEmit = "emit", wAsmNoStackFrame = "asmNoStackFrame", wImplicitStatic = "implicitStatic", wGlobal = "global", wCodegenDecl = "codegenDecl", wUnchecked = "unchecked", wGuard = "guard", wLocks = "locks", wPartial = "partial", wExplain = "explain", wLiftLocals = "liftlocals", wEnforceNoRaises = "enforceNoRaises", + wRedefine = "redefine", wAuto = "auto", wBool = "bool", wCatch = "catch", wChar = "char", - wClass = "class", wCompl = "compl", wConst_cast = "const_cast", wDefault = "default", - wDelete = "delete", wDouble = "double", wDynamic_cast = "dynamic_cast", + wClass = "class", wCompl = "compl", wConstCast = "const_cast", wDefault = "default", + wDelete = "delete", wDouble = "double", wDynamicCast = "dynamic_cast", wExplicit = "explicit", wExtern = "extern", wFalse = "false", wFloat = "float", wFriend = "friend", wGoto = "goto", wInt = "int", wLong = "long", wMutable = "mutable", wNamespace = "namespace", wNew = "new", wOperator = "operator", wPrivate = "private", wProtected = "protected", wPublic = "public", wRegister = "register", - wReinterpret_cast = "reinterpret_cast", wRestrict = "restrict", wShort = "short", - wSigned = "signed", wSizeof = "sizeof", wStatic_cast = "static_cast", wStruct = "struct", + wReinterpretCast = "reinterpret_cast", wRestrict = "restrict", wShort = "short", + wSigned = "signed", wSizeof = "sizeof", wStaticCast = "static_cast", wStruct = "struct", wSwitch = "switch", wThis = "this", wThrow = "throw", wTrue = "true", wTypedef = "typedef", wTypeid = "typeid", wTypeof = "typeof", wTypename = "typename", wUnion = "union", wPacked = "packed", wUnsigned = "unsigned", wVirtual = "virtual", - wVoid = "void", wVolatile = "volatile", wWchar_t = "wchar_t", + wVoid = "void", wVolatile = "volatile", wWchar = "wchar_t", wAlignas = "alignas", wAlignof = "alignof", wConstexpr = "constexpr", wDecltype = "decltype", wNullptr = "nullptr", wNoexcept = "noexcept", - wThread_local = "thread_local", wStatic_assert = "static_assert", - wChar16_t = "char16_t", wChar32_t = "char32_t", + wThreadLocal = "thread_local", wStaticAssert = "static_assert", + wChar16 = "char16_t", wChar32 = "char32_t", wStdIn = "stdin", wStdOut = "stdout", wStdErr = "stderr", @@ -145,4 +146,4 @@ else: for i in a..b: if cmpIgnoreStyle($i, s) == 0: return i - result = default \ No newline at end of file + result = default diff --git a/config/config.nims b/config/config.nims index 08d9d9f555e7..aa1eda8949ec 100644 --- a/config/config.nims +++ b/config/config.nims @@ -20,5 +20,9 @@ when defined(nimStrictMode): switch("hintAsError", "ConvFromXtoItselfNotNeeded") # future work: XDeclaredButNotUsed +when defined(windows) and not defined(booting): + # Avoid some rare stack corruption while using exceptions with a SEH-enabled + # toolchain: https://github.com/nim-lang/Nim/pull/19197 + switch("define", "nimRawSetjmp") + switch("define", "nimVersion:" & NimVersion) -switch("define", "nimPreviewDotLikeOps") diff --git a/config/nim.cfg b/config/nim.cfg index e0b4752efd44..3d4eed476518 100644 --- a/config/nim.cfg +++ b/config/nim.cfg @@ -17,6 +17,8 @@ cc = gcc hint[LineTooLong]=off #hint[XDeclaredButNotUsed]=off +threads:on + # Examples of how to setup a cross-compiler: # Nim can target architectures and OSes different than the local host # Syntax: ..gcc.exe = "" @@ -181,6 +183,9 @@ nimblepath="$home/.nimble/pkgs/" # Configuration for the GNU C/C++ compiler: @if windows: #gcc.path = r"$nim\dist\mingw\bin" + @if gcc or tcc: + tlsEmulation:on + @end @end gcc.maxerrorsimpl = "-fmax-errors=3" @@ -319,12 +324,6 @@ tcc.options.always = "-w" --define:nimEmulateOverflowChecks @end -@if nimv019: - --multimethods:on - --define:nimOldCaseObjects - --define:nimOldShiftRight -@end - @if lto or lto_incremental: @if lto_incremental: vcc.options.always%= "${vcc.options.always} /GL /Gw /Gy" diff --git a/config/nimdoc.cfg b/config/nimdoc.cfg index 4efa1f637ed8..d10c2f3186fe 100644 --- a/config/nimdoc.cfg +++ b/config/nimdoc.cfg @@ -9,25 +9,36 @@ split.item.toc = "20" doc.section = """
-

$sectionTitle

-
-$content -
+

$sectionTitle

+
+ $content +
+ """ -doc.section.toc = """ +# Just a single item in the TOC (e.g. imports, exports) +doc.section.toc_item = """
  • $sectionTitle -
      - $content -
    +
  • +""" + +# This is a section (e.g. procs, types) in the TOC which gets turned into a drop down +doc.section.toc = """ +
  • +
    + $sectionTitle +
      + $content +
    +
  • """ doc.section.toc2 = """ -
      $plainName - $content -
    +
      $plainName + $content +
    """ # Chunk of HTML emitted for each entry in the HTML table of contents. @@ -47,12 +58,12 @@ doc.section.toc2 = """ doc.item = """
    -
    $header
    -
    -$deprecationMsg -$desc -$seeSrc -
    +
    $header
    +
    + $deprecationMsg + $desc + $seeSrc +
    """ @@ -61,9 +72,8 @@ $seeSrc # * $overloadGroupName - the anchor for this whole group # * $content - string containing `doc.item`s themselves doc.item2 = """ -
    -$content + $content
    """ @@ -73,18 +83,14 @@ $content # This is used for TOC items which are not overloadable (e.g. types). # `$header_plain` would be too verbose here, so we use $name. doc.item.toc = """ -
  • $name
  • +
  • $name
  • """ # This is used for TOC items which are grouped by the same name (e.g. procs). doc.item.tocTable = """ -
  • $header_plain
  • +
  • $header_plain
  • """ - - # HTML rendered for doc.item's seeSrc variable. Note that this will render to # the empty string if you don't pass anything through --git.url. Available # substitutaion variables here are: @@ -94,32 +100,31 @@ doc.item.tocTable = """ # * $line: line of the item in the original source file. # * $url: whatever you did pass through the --git.url switch (which also # gets variables path/line replaced!) -doc.item.seesrc = """  Source -  Edit +doc.item.seesrc = """ +Source   +Edit   """ doc.deprecationmsg = """ -
    - $label $message -
    +
    + $label $message +
    """ doc.toc = """
      -$content + $content
    """ doc.body_toc_groupsection = """ -
    - Group by: - -
    +
    + Group by: + +
    """ @if boot: @@ -130,36 +135,37 @@ doc.body_toc_groupsection = """ doc.body_toc_group = """
    -
    - -     Dark Mode -
    - -
    - Search: -
    - $body_toc_groupsection - $tableofcontents +
    + + +
    + +
    + Search: +
    + $body_toc_groupsection + $tableofcontents
    - $seeSrc
    -
    - $deprecationMsg -

    $moduledesc

    - $content + $seeSrc +
    + $deprecationMsg +

    $moduledesc

    + $content
    """ @@ -169,39 +175,37 @@ doc.body_toc_group = """ doc.body_toc_group = """
    -
    - -     Dark Mode -
    - -
    - Search: -
    -
    - Group by: - -
    - $tableofcontents +
    + + +
    + +
    + Search: +
    +
    + Group by: + +
    + $tableofcontents
    - $seeSrc
    -
    - $deprecationMsg -

    $moduledesc

    - $content + $seeSrc +
    + $deprecationMsg +

    $moduledesc

    + $content
    """ @@ -220,14 +224,13 @@ doc.listing_end = "" # * $analytics: Google analytics location, includes - -
    -
    -

    $title

    $subtitle - $content -
    +
    +
    +

    $title

    $subtitle + $content
    -
    -$analytics + $analytics """ diff --git a/doc/advopt.txt b/doc/advopt.txt index 244fe035b8b0..3f439fdab7e9 100644 --- a/doc/advopt.txt +++ b/doc/advopt.txt @@ -127,8 +127,7 @@ Advanced options: --skipParentCfg:on|off do not read the parent dirs' configuration files --skipProjCfg:on|off do not read the project's configuration file --mm:orc|arc|refc|markAndSweep|boehm|go|none|regions - select which memory management to use; default is 'refc' - recommended is 'orc' + select which memory management to use; default is 'orc' --exceptions:setjmp|cpp|goto|quirky select the exception handling implementation --index:on|off turn index file generation on|off diff --git a/doc/apis.rst b/doc/apis.md similarity index 93% rename from doc/apis.rst rename to doc/apis.md index e8313749d7a6..f0b8c93e5669 100644 --- a/doc/apis.rst +++ b/doc/apis.md @@ -18,9 +18,9 @@ been renamed to fit this scheme. The ultimate goal is that the programmer can *guess* a name. -------------------- ------------ -------------------------------------- +=================== ============ ====================================== English word To use Notes -------------------- ------------ -------------------------------------- +=================== ============ ====================================== initialize initT `init` is used to create a value type `T` new newP `new` is used to create a @@ -82,4 +82,4 @@ literal lit string str identifier ident indentation indent -------------------- ------------ -------------------------------------- +=================== ============ ====================================== diff --git a/doc/astspec.txt b/doc/astspec.txt index 6d3fa1f8c203..bfaec71556db 100644 --- a/doc/astspec.txt +++ b/doc/astspec.txt @@ -6,8 +6,7 @@ The AST consists of nodes (``NimNode``) with a variable number of children. Each node has a field named ``kind`` which describes what the node contains: -.. code-block:: nim - + ```nim type NimNodeKind = enum ## kind of a node; only explanatory nnkNone, ## invalid node kind @@ -32,6 +31,7 @@ contains: strVal: string ## the string literal else: sons: seq[NimNode] ## the node's sons (or children) + ``` For the ``NimNode`` type, the ``[]`` operator has been overloaded: ``n[i]`` is ``n``'s ``i``-th child. @@ -50,9 +50,9 @@ A leaf of the AST often corresponds to a terminal symbol in the concrete syntax. Note that the default ``float`` in Nim maps to ``float64`` such that the default AST for a float is ``nnkFloat64Lit`` as below. ------------------ --------------------------------------------- +================= ============================================= Nim expression Corresponding AST ------------------ --------------------------------------------- +================= ============================================= ``42`` ``nnkIntLit(intVal = 42)`` ``42'i8`` ``nnkInt8Lit(intVal = 42)`` ``42'i16`` ``nnkInt16Lit(intVal = 42)`` @@ -72,7 +72,7 @@ Nim expression Corresponding AST ``nil`` ``nnkNilLit()`` ``myIdentifier`` ``nnkIdent(strVal = "myIdentifier")`` ``myIdentifier`` after lookup pass: ``nnkSym(strVal = "myIdentifier", ...)`` ------------------ --------------------------------------------- +================= ============================================= Identifiers are ``nnkIdent`` nodes. After the name lookup pass these nodes get transferred into ``nnkSym`` nodes. @@ -86,17 +86,19 @@ Command call Concrete syntax: -.. code-block:: nim + ```nim echo "abc", "xyz" + ``` AST: -.. code-block:: nim + ```nim nnkCommand( nnkIdent("echo"), nnkStrLit("abc"), nnkStrLit("xyz") ) + ``` Call with ``()`` @@ -104,17 +106,19 @@ Call with ``()`` Concrete syntax: -.. code-block:: nim + ```nim echo("abc", "xyz") + ``` AST: -.. code-block:: nim + ```nim nnkCall( nnkIdent("echo"), nnkStrLit("abc"), nnkStrLit("xyz") ) + ``` Infix operator call @@ -122,29 +126,32 @@ Infix operator call Concrete syntax: -.. code-block:: nim + ```nim "abc" & "xyz" + ``` AST: -.. code-block:: nim + ```nim nnkInfix( nnkIdent("&"), nnkStrLit("abc"), nnkStrLit("xyz") ) + ``` Note that with multiple infix operators, the command is parsed by operator precedence. Concrete syntax: -.. code-block:: nim + ```nim 5 + 3 * 4 + ``` AST: -.. code-block:: nim + ```nim nnkInfix( nnkIdent("+"), nnkIntLit(5), @@ -154,6 +161,7 @@ AST: nnkIntLit(4) ) ) + ``` As a side note, if you choose to use infix operators in a prefix form, the AST behaves as a @@ -162,12 +170,13 @@ behaves as a Concrete syntax: -.. code-block:: nim + ```nim `+`(3, 4) + ``` AST: -.. code-block:: nim + ```nim nnkCall( nnkAccQuoted( nnkIdent("+") @@ -175,22 +184,25 @@ AST: nnkIntLit(3), nnkIntLit(4) ) + ``` Prefix operator call -------------------- Concrete syntax: -.. code-block:: nim + ```nim ? "xyz" + ``` AST: -.. code-block:: nim + ```nim nnkPrefix( nnkIdent("?"), nnkStrLit("abc") ) + ``` Postfix operator call @@ -201,16 +213,18 @@ Postfix operator call Concrete syntax: -.. code-block:: nim + ```nim identifier* + ``` AST: -.. code-block:: nim + ```nim nnkPostfix( nnkIdent("*"), nnkIdent("identifier") ) + ``` Call with named arguments @@ -218,12 +232,13 @@ Call with named arguments Concrete syntax: -.. code-block:: nim + ```nim writeLine(file=stdout, "hallo") + ``` AST: -.. code-block:: nim + ```nim nnkCall( nnkIdent("writeLine"), nnkExprEqExpr( @@ -232,6 +247,7 @@ AST: ), nnkStrLit("hallo") ) + ``` Call with raw string literal ---------------------------- @@ -242,29 +258,33 @@ This is used, for example, in the ``bindSym`` examples Concrete syntax: -.. code-block:: nim + ```nim echo"abc" + ``` AST: -.. code-block:: nim + ```nim nnkCallStrLit( nnkIdent("echo"), nnkRStrLit("hello") ) + ``` Dereference operator ``[]`` --------------------------- Concrete syntax: -.. code-block:: nim + ```nim x[] + ``` AST: -.. code-block:: nim + ```nim nnkDerefExpr(nnkIdent("x")) + ``` Addr operator @@ -272,13 +292,15 @@ Addr operator Concrete syntax: -.. code-block:: nim + ```nim addr(x) + ``` AST: -.. code-block:: nim + ```nim nnkAddr(nnkIdent("x")) + ``` Cast operator @@ -286,13 +308,15 @@ Cast operator Concrete syntax: -.. code-block:: nim + ```nim cast[T](x) + ``` AST: -.. code-block:: nim + ```nim nnkCast(nnkIdent("T"), nnkIdent("x")) + ``` Object access operator ``.`` @@ -300,13 +324,15 @@ Object access operator ``.`` Concrete syntax: -.. code-block:: nim + ```nim x.y + ``` AST: -.. code-block:: nim + ```nim nnkDotExpr(nnkIdent("x"), nnkIdent("y")) + ``` If you use Nim's flexible calling syntax (as in ``x.len()``), the result is the same as above but wrapped in an ``nnkCall``. @@ -317,13 +343,15 @@ Array access operator ``[]`` Concrete syntax: -.. code-block:: nim + ```nim x[y] + ``` AST: -.. code-block:: nim + ```nim nnkBracketExpr(nnkIdent("x"), nnkIdent("y")) + ``` Parentheses @@ -333,16 +361,18 @@ Parentheses for affecting operator precedence use the ``nnkPar`` node. Concrete syntax: -.. code-block:: nim + ```nim (a + b) * c + ``` AST: -.. code-block:: nim + ```nim nnkInfix(nnkIdent("*"), nnkPar( nnkInfix(nnkIdent("+"), nnkIdent("a"), nnkIdent("b"))), nnkIdent("c")) + ``` Tuple Constructors ------------------ @@ -351,35 +381,39 @@ Nodes for tuple construction are built with the ``nnkTupleConstr`` node. Concrete syntax: -.. code-block:: nim + ```nim (1, 2, 3) (a: 1, b: 2, c: 3) () + ``` AST: -.. code-block:: nim + ```nim nnkTupleConstr(nnkIntLit(1), nnkIntLit(2), nnkIntLit(3)) nnkTupleConstr( nnkExprColonExpr(nnkIdent("a"), nnkIntLit(1)), nnkExprColonExpr(nnkIdent("b"), nnkIntLit(2)), nnkExprColonExpr(nnkIdent("c"), nnkIntLit(3))) nnkTupleConstr() + ``` Since the one tuple would be syntactically identical to parentheses with an expression in them, the parser expects a trailing comma for them. For tuple constructors with field names, this is not necessary. -.. code-block:: nim + ```nim (1,) (a: 1) + ``` AST: -.. code-block:: nim + ```nim nnkTupleConstr(nnkIntLit(1)) nnkTupleConstr( nnkExprColonExpr(nnkIdent("a"), nnkIntLit(1))) + ``` Curly braces ------------ @@ -388,28 +422,32 @@ Curly braces are used as the set constructor. Concrete syntax: -.. code-block:: nim + ```nim {1, 2, 3} + ``` AST: -.. code-block:: nim + ```nim nnkCurly(nnkIntLit(1), nnkIntLit(2), nnkIntLit(3)) + ``` When used as a table constructor, the syntax is different. Concrete syntax: -.. code-block:: nim + ```nim {a: 3, b: 5} + ``` AST: -.. code-block:: nim + ```nim nnkTableConstr( nnkExprColonExpr(nnkIdent("a"), nnkIntLit(3)), nnkExprColonExpr(nnkIdent("b"), nnkIntLit(5)) ) + ``` Brackets @@ -419,13 +457,15 @@ Brackets are used as the array constructor. Concrete syntax: -.. code-block:: nim + ```nim [1, 2, 3] + ``` AST: -.. code-block:: nim + ```nim nnkBracket(nnkIntLit(1), nnkIntLit(2), nnkIntLit(3)) + ``` Ranges @@ -437,21 +477,23 @@ AST, construction with ``..`` as an infix operator should be used instead. Concrete syntax: -.. code-block:: nim + ```nim 1..3 + ``` AST: -.. code-block:: nim + ```nim nnkInfix( nnkIdent(".."), nnkIntLit(1), nnkIntLit(3) ) + ``` Example code: -.. code-block:: nim + ```nim macro genRepeatEcho() = result = newNimNode(nnkStmtList) @@ -470,6 +512,7 @@ Example code: # 3 # 3 # 3 + ``` If expression @@ -479,17 +522,19 @@ The representation of the ``if`` expression is subtle, but easy to traverse. Concrete syntax: -.. code-block:: nim + ```nim if cond1: expr1 elif cond2: expr2 else: expr3 + ``` AST: -.. code-block:: nim + ```nim nnkIfExpr( nnkElifExpr(cond1, expr1), nnkElifExpr(cond2, expr2), nnkElseExpr(expr3) ) + ``` Documentation Comments ---------------------- @@ -500,19 +545,21 @@ comments are ignored. Concrete syntax: -.. code-block:: nim + ```nim ## This is a comment ## This is part of the first comment stmt1 ## Yet another + ``` AST: -.. code-block:: nim + ```nim nnkCommentStmt() # only appears once for the first two lines! stmt1 nnkCommentStmt() # another nnkCommentStmt because there is another comment # (separate from the first) + ``` Pragmas ------- @@ -523,30 +570,33 @@ objects, but the standalone ``emit`` pragma shows the basics with the AST. Concrete syntax: -.. code-block:: nim + ```nim {.emit: "#include ".} + ``` AST: -.. code-block:: nim + ```nim nnkPragma( nnkExprColonExpr( nnkIdent("emit"), nnkStrLit("#include ") # the "argument" ) ) + ``` As many ``nnkIdent`` appear as there are pragmas between ``{..}``. Note that the declaration of new pragmas is essentially the same: Concrete syntax: -.. code-block:: nim + ```nim {.pragma: cdeclRename, cdecl.} + ``` AST: -.. code-block:: nim + ```nim nnkPragma( nnkExprColonExpr( nnkIdent("pragma"), # this is always first when declaring a new pragma @@ -554,6 +604,7 @@ AST: ), nnkIdent("cdecl") ) + ``` Statements ========== @@ -566,7 +617,7 @@ there is no ``else`` branch, no ``nnkElse`` child exists. Concrete syntax: -.. code-block:: nim + ```nim if cond1: stmt1 elif cond2: @@ -575,16 +626,18 @@ Concrete syntax: stmt3 else: stmt4 + ``` AST: -.. code-block:: nim + ```nim nnkIfStmt( nnkElifBranch(cond1, stmt1), nnkElifBranch(cond2, stmt2), nnkElifBranch(cond3, stmt3), nnkElse(stmt4) ) + ``` When statement @@ -598,13 +651,15 @@ Assignment Concrete syntax: -.. code-block:: nim + ```nim x = 42 + ``` AST: -.. code-block:: nim + ```nim nnkAsgn(nnkIdent("x"), nnkIntLit(42)) + ``` This is not the syntax for assignment when combined with ``var``, ``let``, or ``const``. @@ -614,15 +669,17 @@ Statement list Concrete syntax: -.. code-block:: nim + ```nim stmt1 stmt2 stmt3 + ``` AST: -.. code-block:: nim + ```nim nnkStmtList(stmt1, stmt2, stmt3) + ``` Case statement @@ -630,7 +687,7 @@ Case statement Concrete syntax: -.. code-block:: nim + ```nim case expr1 of expr2, expr3..expr4: stmt1 @@ -640,10 +697,11 @@ Concrete syntax: stmt3 else: stmt4 + ``` AST: -.. code-block:: nim + ```nim nnkCaseStmt( expr1, nnkOfBranch(expr2, nnkRange(expr3, expr4), stmt1), @@ -651,6 +709,7 @@ AST: nnkElifBranch(cond1, stmt3), nnkElse(stmt4) ) + ``` The ``nnkElifBranch`` and ``nnkElse`` parts may be missing. @@ -660,14 +719,16 @@ While statement Concrete syntax: -.. code-block:: nim + ```nim while expr1: stmt1 + ``` AST: -.. code-block:: nim + ```nim nnkWhileStmt(expr1, stmt1) + ``` For statement @@ -675,14 +736,16 @@ For statement Concrete syntax: -.. code-block:: nim + ```nim for ident1, ident2 in expr1: stmt1 + ``` AST: -.. code-block:: nim + ```nim nnkForStmt(ident1, ident2, expr1, stmt1) + ``` Try statement @@ -690,7 +753,7 @@ Try statement Concrete syntax: -.. code-block:: nim + ```nim try: stmt1 except e1, e2: @@ -701,10 +764,11 @@ Concrete syntax: stmt4 finally: stmt5 + ``` AST: -.. code-block:: nim + ```nim nnkTryStmt( stmt1, nnkExceptBranch(e1, e2, stmt2), @@ -712,6 +776,7 @@ AST: nnkExceptBranch(stmt4), nnkFinally(stmt5) ) + ``` Return statement @@ -719,13 +784,15 @@ Return statement Concrete syntax: -.. code-block:: nim + ```nim return expr1 + ``` AST: -.. code-block:: nim + ```nim nnkReturnStmt(expr1) + ``` Yield statement @@ -733,8 +800,9 @@ Yield statement Like ``return``, but with ``nnkYieldStmt`` kind. -.. code-block:: nim + ```nim nnkYieldStmt(expr1) + ``` Discard statement @@ -742,8 +810,9 @@ Discard statement Like ``return``, but with ``nnkDiscardStmt`` kind. -.. code-block:: nim + ```nim nnkDiscardStmt(expr1) + ``` Continue statement @@ -751,26 +820,30 @@ Continue statement Concrete syntax: -.. code-block:: nim + ```nim continue + ``` AST: -.. code-block:: nim + ```nim nnkContinueStmt() + ``` Break statement --------------- Concrete syntax: -.. code-block:: nim + ```nim break otherLocation + ``` AST: -.. code-block:: nim + ```nim nnkBreakStmt(nnkIdent("otherLocation")) + ``` If ``break`` is used without a jump-to location, ``nnkEmpty`` replaces ``nnkIdent``. @@ -779,13 +852,15 @@ Block statement Concrete syntax: -.. code-block:: nim + ```nim block name: + ``` AST: -.. code-block:: nim + ```nim nnkBlockStmt(nnkIdent("name"), nnkStmtList(...)) + ``` A ``block`` doesn't need an name, in which case ``nnkEmpty`` is used. @@ -794,18 +869,20 @@ Asm statement Concrete syntax: -.. code-block:: nim + ```nim asm """ some asm """ + ``` AST: -.. code-block:: nim + ```nim nnkAsmStmt( nnkEmpty(), # for pragmas nnkTripleStrLit("some asm"), ) + ``` Import section -------------- @@ -815,37 +892,42 @@ on what keywords are present. Let's start with the simplest form. Concrete syntax: -.. code-block:: nim + ```nim import math + ``` AST: -.. code-block:: nim + ```nim nnkImportStmt(nnkIdent("math")) + ``` With ``except``, we get ``nnkImportExceptStmt``. Concrete syntax: -.. code-block:: nim + ```nim import math except pow + ``` AST: -.. code-block:: nim + ```nim nnkImportExceptStmt(nnkIdent("math"),nnkIdent("pow")) + ``` Note that ``import math as m`` does not use a different node; rather, we use ``nnkImportStmt`` with ``as`` as an infix operator. Concrete syntax: -.. code-block:: nim + ```nim import strutils as su + ``` AST: -.. code-block:: nim + ```nim nnkImportStmt( nnkInfix( nnkIdent("as"), @@ -853,6 +935,7 @@ AST: nnkIdent("su") ) ) + ``` From statement -------------- @@ -861,13 +944,15 @@ If we use ``from ... import``, the result is different, too. Concrete syntax: -.. code-block:: nim + ```nim from math import pow + ``` AST: -.. code-block:: nim + ```nim nnkFromStmt(nnkIdent("math"), nnkIdent("pow")) + ``` Using ``from math as m import pow`` works identically to the ``as`` modifier with the ``import`` statement, but wrapped in ``nnkFromStmt``. @@ -880,26 +965,30 @@ the ``export`` syntax is pretty straightforward. Concrete syntax: -.. code-block:: nim + ```nim export unsigned + ``` AST: -.. code-block:: nim + ```nim nnkExportStmt(nnkIdent("unsigned")) + ``` Similar to the ``import`` statement, the AST is different for ``export ... except``. Concrete syntax: -.. code-block:: nim + ```nim export math except pow # we're going to implement our own exponentiation + ``` AST: -.. code-block:: nim + ```nim nnkExportExceptStmt(nnkIdent("math"),nnkIdent("pow")) + ``` Include statement ----------------- @@ -908,25 +997,28 @@ Like a plain ``import`` statement but with ``nnkIncludeStmt``. Concrete syntax: -.. code-block:: nim + ```nim include blocks + ``` AST: -.. code-block:: nim + ```nim nnkIncludeStmt(nnkIdent("blocks")) + ``` Var section ----------- Concrete syntax: -.. code-block:: nim + ```nim var a = 3 + ``` AST: -.. code-block:: nim + ```nim nnkVarSection( nnkIdentDefs( nnkIdent("a"), @@ -934,6 +1026,7 @@ AST: nnkIntLit(3), ) ) + ``` Note that either the second or third (or both) parameters above must exist, as the compiler needs to know the type somehow (which it can infer from @@ -951,12 +1044,13 @@ This is equivalent to ``var``, but with ``nnkLetSection`` rather than Concrete syntax: -.. code-block:: nim + ```nim let a = 3 + ``` AST: -.. code-block:: nim + ```nim nnkLetSection( nnkIdentDefs( nnkIdent("a"), @@ -964,18 +1058,20 @@ AST: nnkIntLit(3), ) ) + ``` Const section ------------- Concrete syntax: -.. code-block:: nim + ```nim const a = 3 + ``` AST: -.. code-block:: nim + ```nim nnkConstSection( nnkConstDef( # not nnkConstDefs! nnkIdent("a"), @@ -983,6 +1079,7 @@ AST: nnkIntLit(3), # required in a const declaration! ) ) + ``` Type section ------------ @@ -992,12 +1089,13 @@ and ``const``. Concrete syntax: -.. code-block:: nim + ```nim type A = int + ``` AST: -.. code-block:: nim + ```nim nnkTypeSection( nnkTypeDef( nnkIdent("A"), @@ -1005,18 +1103,20 @@ AST: nnkIdent("int") ) ) + ``` Declaring ``distinct`` types is similar, with the last ``nnkIdent`` wrapped in ``nnkDistinctTy``. Concrete syntax: -.. code-block:: nim + ```nim type MyInt = distinct int + ``` AST: -.. code-block:: nim + ```nim # ... nnkTypeDef( nnkIdent("MyInt"), @@ -1025,17 +1125,19 @@ AST: nnkIdent("int") ) ) + ``` If a type section uses generic parameters, they are treated here: Concrete syntax: -.. code-block:: nim + ```nim type A[T] = expr1 + ``` AST: -.. code-block:: nim + ```nim nnkTypeSection( nnkTypeDef( nnkIdent("A"), @@ -1050,6 +1152,7 @@ AST: expr1, ) ) + ``` Note that not all ``nnkTypeDef`` utilize ``nnkIdent`` as their parameter. One of the most common uses of type declarations @@ -1057,12 +1160,13 @@ is to work with objects. Concrete syntax: -.. code-block:: nim + ```nim type IO = object of RootObj + ``` AST: -.. code-block:: nim + ```nim # ... nnkTypeDef( nnkIdent("IO"), @@ -1075,13 +1179,14 @@ AST: nnkEmpty() ) ) + ``` Nim's object syntax is rich. Let's take a look at an involved example in its entirety to see some of the complexities. Concrete syntax: -.. code-block:: nim + ```nim type Obj[T] {.inheritable.} = object name: string case isFat: bool @@ -1089,10 +1194,11 @@ Concrete syntax: m: array[100_000, T] of false: m: array[10, T] + ``` AST: -.. code-block:: nim + ```nim # ... nnkPragmaExpr( nnkIdent("Obj"), @@ -1149,36 +1255,40 @@ AST: ) ) ) + ``` Using an ``enum`` is similar to using an ``object``. Concrete syntax: -.. code-block:: nim + ```nim type X = enum First + ``` AST: -.. code-block:: nim + ```nim # ... nnkEnumTy( nnkEmpty(), nnkIdent("First") # you need at least one nnkIdent or the compiler complains ) + ``` The usage of ``concept`` (experimental) is similar to objects. Concrete syntax: -.. code-block:: nim + ```nim type Con = concept x,y,z (x & y & z) is string + ``` AST: -.. code-block:: nim + ```nim # ... nnkTypeClassTy( # note this isn't nnkConceptTy! nnkArgList( @@ -1186,18 +1296,20 @@ AST: ) # ... ) + ``` Static types, like ``static[int]``, use ``nnkIdent`` wrapped in ``nnkStaticTy``. Concrete syntax: -.. code-block:: nim + ```nim type A[T: static[int]] = object + ``` AST: -.. code-block:: nim + ```nim # ... within nnkGenericParams nnkIdentDefs( nnkIdent("T"), @@ -1207,13 +1319,14 @@ AST: nnkEmpty() ) # ... + ``` In general, declaring types mirrors this syntax (i.e., ``nnkStaticTy`` for ``static``, etc.). Examples follow (exceptions marked by ``*``): -------------- --------------------------------------------- +============= ============================================= Nim type Corresponding AST -------------- --------------------------------------------- +============= ============================================= ``static`` ``nnkStaticTy`` ``tuple`` ``nnkTupleTy`` ``var`` ``nnkVarTy`` @@ -1226,7 +1339,7 @@ Nim type Corresponding AST ``proc`` ``nnkProcTy`` ``iterator`` ``nnkIteratorTy`` ``object`` ``nnkObjectTy`` -------------- --------------------------------------------- +============= ============================================= Take special care when declaring types as ``proc``. The behavior is similar to ``Procedure declaration``, below, but does not treat ``nnkGenericParams``. @@ -1234,12 +1347,13 @@ Generic parameters are treated in the type, not the ``proc`` itself. Concrete syntax: -.. code-block:: nim + ```nim type MyProc[T] = proc(x: T) + ``` AST: -.. code-block:: nim + ```nim # ... nnkTypeDef( nnkIdent("MyProc"), @@ -1252,6 +1366,7 @@ AST: ) ) ) + ``` The same syntax applies to ``iterator`` (with ``nnkIteratorTy``), but *does not* apply to ``converter`` or ``template``. @@ -1261,26 +1376,30 @@ Mixin statement Concrete syntax: -.. code-block:: nim + ```nim mixin x + ``` AST: -.. code-block:: nim + ```nim nnkMixinStmt(nnkIdent("x")) + ``` Bind statement -------------- Concrete syntax: -.. code-block:: nim + ```nim bind x + ``` AST: -.. code-block:: nim + ```nim nnkBindStmt(nnkIdent("x")) + ``` Procedure declaration --------------------- @@ -1290,12 +1409,13 @@ a feel for how procedure calls are broken down. Concrete syntax: -.. code-block:: nim + ```nim proc hello*[T: SomeInteger](x: int = 3, y: float32): int {.inline.} = discard + ``` AST: -.. code-block:: nim + ```nim nnkProcDef( nnkPostfix(nnkIdent("*"), nnkIdent("hello")), # the exported proc name nnkEmpty(), # patterns for term rewriting in templates and macros (not procs) @@ -1323,6 +1443,7 @@ AST: nnkEmpty(), # reserved slot for future use nnkStmtList(nnkDiscardStmt(nnkEmpty())) # the meat of the proc ) + ``` There is another consideration. Nim has flexible type identification for its procs. Even though ``proc(a: int, b: int)`` and ``proc(a, b: int)`` @@ -1330,12 +1451,13 @@ are equivalent in the code, the AST is a little different for the latter. Concrete syntax: -.. code-block:: nim + ```nim proc(a, b: int) + ``` AST: -.. code-block:: nim + ```nim # ...AST as above... nnkFormalParams( nnkEmpty(), # no return here @@ -1347,24 +1469,27 @@ AST: ) ), # ... + ``` When a procedure uses the special ``var`` type return variable, the result is different from that of a var section. Concrete syntax: -.. code-block:: nim + ```nim proc hello(): var int + ``` AST: -.. code-block:: nim + ```nim # ... nnkFormalParams( nnkVarTy( nnkIdent("int") ) ) + ``` Iterator declaration -------------------- @@ -1374,17 +1499,19 @@ replacing ``nnkProcDef``. Concrete syntax: -.. code-block:: nim + ```nim iterator nonsense[T](x: seq[T]): float {.closure.} = ... + ``` AST: -.. code-block:: nim + ```nim nnkIteratorDef( nnkIdent("nonsense"), nnkEmpty(), ... ) + ``` Converter declaration --------------------- @@ -1393,16 +1520,18 @@ A converter is similar to a proc. Concrete syntax: -.. code-block:: nim + ```nim converter toBool(x: float): bool + ``` AST: -.. code-block:: nim + ```nim nnkConverterDef( nnkIdent("toBool"), # ... ) + ``` Template declaration -------------------- @@ -1415,12 +1544,13 @@ the ``nnkEmpty()`` as the second argument to ``nnkProcDef`` and Concrete syntax: -.. code-block:: nim + ```nim template optOpt{expr1}(a: int): int + ``` AST: -.. code-block:: nim + ```nim nnkTemplateDef( nnkIdent("optOpt"), nnkStmtList( # instead of nnkEmpty() @@ -1428,6 +1558,7 @@ AST: ), # follows like a proc or iterator ) + ``` If the template does not have types for its parameters, the type identifiers inside ``nnkFormalParams`` just becomes ``nnkEmpty``. @@ -1441,8 +1572,9 @@ Macros behave like templates, but ``nnkTemplateDef`` is replaced with Hidden Standard Conversion -------------------------- -.. code-block:: nim + ```nim var f: float = 1 + ``` The type of "f" is ``float`` but the type of "1" is actually ``int``. Inserting ``int`` into a ``float`` is a type error. Nim inserts the ``nnkHiddenStdConv`` diff --git a/doc/backends.rst b/doc/backends.md similarity index 77% rename from doc/backends.rst rename to doc/backends.md index 65dd8a6f24c6..5258e9b4dc8a 100644 --- a/doc/backends.rst +++ b/doc/backends.md @@ -10,13 +10,14 @@ .. no syntax highlighting here by default: .. contents:: - "Heresy grows from idleness." -- Unknown. + +> "Heresy grows from idleness." -- Unknown. Introduction ============ -The `Nim Compiler User Guide `_ documents the typical +The [Nim Compiler User Guide](nimc.html) documents the typical compiler invocation, using the `compile`:option: or `c`:option: command to transform a ``.nim`` file into one or more ``.c`` files which are then compiled with the @@ -25,12 +26,12 @@ to compile to C++, Objective-C, or JavaScript. This document tries to concentrate in a single place all the backend and interfacing options. The Nim compiler supports mainly two backend families: the C, C++ and -Objective-C targets and the JavaScript target. `The C like targets -<#backends-the-c-like-targets>`_ creates source files that can be compiled -into a library or a final executable. `The JavaScript target -<#backends-the-javascript-target>`_ can generate a ``.js`` file which you -reference from an HTML file or create a `standalone Node.js program -`_. +Objective-C targets and the JavaScript target. [The C like targets]( +#backends-the-c-like-targets) creates source files that can be compiled +into a library or a final executable. [The JavaScript target]( +#backends-the-javascript-target) can generate a ``.js`` file which you +reference from an HTML file or create a [standalone Node.js program]( +http://nodejs.org). On top of generating libraries or standalone applications, Nim offers bidirectional interfacing with the backend targets through generic and @@ -56,15 +57,15 @@ project. This allows you to take the generated code and place it directly into a project using any of these languages. Here are some typical command- line invocations: -.. code:: cmd - - nim c hallo.nim - nim cpp hallo.nim - nim objc hallo.nim + ```cmd + nim c hallo.nim + nim cpp hallo.nim + nim objc hallo.nim + ``` The compiler commands select the target backend, but if needed you can -`specify additional switches for cross-compilation -`_ to select the target CPU, operative system +[specify additional switches for cross-compilation]( +nimc.html#crossminuscompilation) to select the target CPU, operative system or compiler/linker commands. @@ -88,19 +89,19 @@ available. This includes: * some modules of the standard library * proper 64-bit integer arithmetic -To compensate, the standard library has modules `catered to the JS backend -`_ +To compensate, the standard library has modules [catered to the JS backend]( +lib.html#pure-libraries-modules-for-js-backend) and more support will come in the future (for instance, Node.js bindings to get OS info). To compile a Nim module into a ``.js`` file use the `js`:option: command; the default is a ``.js`` file that is supposed to be referenced in an ``.html`` file. However, you can also run the code with `nodejs`:idx: -(``_): - -.. code:: cmd +(http://nodejs.org): + ```cmd nim js -d:nodejs -r examples/hallo.nim + ``` If you experience errors saying that `globalThis` is not defined, be sure to run a recent version of Node.js (at least 12.0). @@ -119,14 +120,14 @@ component?). Nim code calling the backend ---------------------------- -Nim code can interface with the backend through the `Foreign function -interface `_ mainly through the -`importc pragma `_. +Nim code can interface with the backend through the [Foreign function +interface](manual.html#foreign-function-interface) mainly through the +[importc pragma](manual.html#foreign-function-interface-importc-pragma). The `importc` pragma is the *generic* way of making backend symbols available in Nim and is available in all the target backends (JavaScript too). The C++ -or Objective-C backends have their respective `ImportCpp -`_ and -`ImportObjC `_ +or Objective-C backends have their respective [ImportCpp]( +manual.html#implementation-specific-pragmas-importcpp-pragma) and +[ImportObjC](manual.html#implementation-specific-pragmas-importobjc-pragma) pragmas to call methods from classes. Whenever you use any of these pragmas you need to integrate native code into @@ -138,42 +139,43 @@ However, for the C like targets you need to link external code either statically or dynamically. The preferred way of integrating native code is to use dynamic linking because it allows you to compile Nim programs without the need for having the related development libraries installed. This is done -through the `dynlib pragma for import -`_, though -more specific control can be gained using the `dynlib module `_. +through the [dynlib pragma for import]( +manual.html#foreign-function-interface-dynlib-pragma-for-import), though +more specific control can be gained using the [dynlib module](dynlib.html). -The `dynlibOverride `_ command line switch allows +The [dynlibOverride](nimc.html#dynliboverride) command line switch allows to avoid dynamic linking if you need to statically link something instead. -Nim wrappers designed to statically link source files can use the `compile -pragma `_ if +Nim wrappers designed to statically link source files can use the [compile +pragma](manual.html#implementation-specific-pragmas-compile-pragma) if there are few sources or providing them along the Nim code is easier than using a system library. Libraries installed on the host system can be linked in with -the `PassL pragma `_. +the [PassL pragma](manual.html#implementation-specific-pragmas-passl-pragma). -To wrap native code, take a look at the `c2nim tool `_ which helps +To wrap native code, take a look at the [c2nim tool]( +https://github.com/nim-lang/c2nim/blob/master/doc/c2nim.rst) which helps with the process of scanning and transforming header files into a Nim interface. -C invocation example -~~~~~~~~~~~~~~~~~~~~ +### C invocation example Create a ``logic.c`` file with the following content: -.. code-block:: c + ```c int addTwoIntegers(int a, int b) { return a + b; } + ``` Create a ``calculator.nim`` file with the following content: -.. code-block:: nim - + ```nim {.compile: "logic.c".} proc addTwoIntegers(a, b: cint): cint {.importc.} when isMainModule: echo addTwoIntegers(3, 7) + ``` With these two files in place, you can run `nim c -r calculator.nim`:cmd: and the Nim compiler will compile the ``logic.c`` file in addition to @@ -182,24 +184,22 @@ run. Another way to link the C file statically and get the same effect would be to remove the line with the `compile` pragma and run the following typical Unix commands: -.. code:: cmd - - gcc -c logic.c - ar rvs mylib.a logic.o - nim c --passL:mylib.a -r calculator.nim + ```cmd + gcc -c logic.c + ar rvs mylib.a logic.o + nim c --passL:mylib.a -r calculator.nim + ``` Just like in this example we pass the path to the ``mylib.a`` library (and we could as well pass ``logic.o``) we could be passing switches to link any other static C library. -JavaScript invocation example -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +### JavaScript invocation example Create a ``host.html`` file with the following content: -.. code-block:: - + ``` + ``` Create a ``calculator.nim`` file with the following content (or reuse the one from the previous section): -.. code-block:: nim - + ```nim proc addTwoIntegers(a, b: int): int {.importc.} when isMainModule: echo addTwoIntegers(3, 7) + ``` Compile the Nim code to JavaScript with `nim js -o:calculator.js calculator.nim`:cmd: and open ``host.html`` in a browser. If the browser supports javascript, you should see the value `10` in the browser's console. Use the -`dom module `_ for specific DOM querying and modification procs -or take a look at `karax `_ for how to +[dom module](dom.html) for specific DOM querying and modification procs +or take a look at [karax](https://github.com/pragmagic/karax) for how to develop browser-based applications. Backend code calling Nim ------------------------ -Backend code can interface with Nim code exposed through the `exportc -pragma `_. The +Backend code can interface with Nim code exposed through the [exportc +pragma](manual.html#foreign-function-interface-exportc-pragma). The `exportc` pragma is the *generic* way of making Nim symbols available to the backends. By default, the Nim compiler will mangle all the Nim symbols to avoid any name collision, so the most significant thing the `exportc` pragma @@ -250,26 +251,25 @@ The name `NimMain` can be influenced via the `--nimMainPrefix:prefix` switch. Use `--nimMainPrefix:MyLib` and the function to call is named `MyLibNimMain`. -Nim invocation example from C -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +### Nim invocation example from C Create a ``fib.nim`` file with the following content: -.. code-block:: nim - + ```nim proc fib(a: cint): cint {.exportc.} = if a <= 2: result = 1 else: result = fib(a - 1) + fib(a - 2) + ``` Create a ``maths.c`` file with the following content: -.. code-block:: c - + ```c #include - extern int fib(int a); + int fib(int a); + void NimMain(); int main(void) { @@ -278,15 +278,16 @@ Create a ``maths.c`` file with the following content: printf("Fib of %d is %d\n", f, fib(f)); return 0; } + ``` Now you can run the following Unix like commands to first generate C sources from the Nim code, then link them into a static binary along your main C program: -.. code:: cmd - + ```cmd nim c --noMain --noLinking fib.nim 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()`:c: function in the generated files and to avoid linking the @@ -298,10 +299,10 @@ have to tell the C compiler where to find Nim's ``nimbase.h`` header file. Instead of depending on the generation of the individual ``.c`` files you can also ask the Nim compiler to generate a statically linked library: -.. code:: cmd - + ```cmd nim c --app:staticLib --noMain fib.nim - gcc -o m -Inimcache -Ipath/to/nim/lib libfib.nim.a maths.c + gcc -o m -Inimcache -Ipath/to/nim/lib maths.c libfib.nim.a + ``` The Nim compiler will handle linking the source files generated in the ``nimcache`` directory into the ``libfib.nim.a`` static library, which you can @@ -310,30 +311,29 @@ vary for each system. For instance, on Linux systems you will likely need to use `-ldl`:option: too to link in required dlopen functionality. -Nim invocation example from JavaScript -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +### Nim invocation example from JavaScript Create a ``mhost.html`` file with the following content: -.. code-block:: - + ``` + ``` Create a ``fib.nim`` file with the following content (or reuse the one from the previous section): -.. code-block:: nim - + ```nim proc fib(a: cint): cint {.exportc.} = if a <= 2: result = 1 else: result = fib(a - 1) + fib(a - 2) + ``` Compile the Nim code to JavaScript with `nim js -o:fib.js fib.nim`:cmd: and open ``mhost.html`` in a browser. If the browser supports javascript, you @@ -348,8 +348,8 @@ Nimcache naming logic The `nimcache`:idx: directory is generated during compilation and will hold either temporary or final files depending on your backend target. The default name for the directory depends on the used backend and on your OS but you can -use the `--nimcache`:option: `compiler switch -`_ to change it. +use the `--nimcache`:option: [compiler switch]( +nimc.html#compiler-usage-commandminusline-switches) to change it. Memory management @@ -367,23 +367,23 @@ aware of who controls what to avoid crashing. Strings and C strings --------------------- -The manual mentions that `Nim strings are implicitly convertible to -cstrings `_ which makes interaction usually +The manual mentions that [Nim strings are implicitly convertible to +cstrings](manual.html#types-cstring-type) which makes interaction usually painless. Most C functions accepting a Nim string converted to a `cstring` will likely not need to keep this string around and by the time they return the string won't be needed anymore. However, for the rare cases where a Nim string has to be preserved and made available to the C backend as a `cstring`, you will need to manually prevent the string data -from being freed with `GC_ref `_ and `GC_unref -`_. +from being freed with [GC_ref](system.html#GC_ref,string) and [GC_unref]( +system.html#GC_unref,string). A similar thing happens with C code invoking Nim code which returns a `cstring`. Consider the following proc: -.. code-block:: nim - + ```nim proc gimme(): cstring {.exportc.} = result = "Hey there C code! " & $rand(100) + ``` Since Nim's reference counting mechanism is not aware of the C code, once the `gimme` proc has finished it can reclaim the memory of the `cstring`. @@ -394,10 +394,10 @@ Custom data types Just like strings, custom data types that are to be shared between Nim and the backend will need careful consideration of who controls who. If you want -to hand a Nim reference to C code, you will need to use `GC_ref -`_ to mark the reference as used, so it does not get -freed. And for the C backend you will need to expose the `GC_unref -`_ proc to clean up this memory when it is not +to hand a Nim reference to C code, you will need to use [GC_ref]( +system.html#GC_ref,ref.T) to mark the reference as used, so it does not get +freed. And for the C backend you will need to expose the [GC_unref]( +system.html#GC_unref,ref.T) proc to clean up this memory when it is not required anymore. Again, if you are wrapping a library which *mallocs* and *frees* data diff --git a/doc/basicopt.txt b/doc/basicopt.txt index 390fc6fd23d0..a9aa4b8fae53 100644 --- a/doc/basicopt.txt +++ b/doc/basicopt.txt @@ -4,7 +4,7 @@ Command: //compile, c compile project with default code generator (C) - //r compile to $nimcache/projname, run with [arguments] + //r compile to $nimcache/projname, run with `arguments` using backend specified by `--backend` (default: c) //doc generate the documentation for inputfile for backend specified by `--backend` (default: c) diff --git a/doc/contributing.rst b/doc/contributing.md similarity index 86% rename from doc/contributing.rst rename to doc/contributing.md index d3b2770f301b..83ca8566368d 100644 --- a/doc/contributing.rst +++ b/doc/contributing.md @@ -8,7 +8,7 @@ Contributing .. contents:: -Contributing happens via "Pull requests" (PR) on github. Every PR needs to be +Contributing happens via "Pull requests" (PR) on GitHub. Every PR needs to be reviewed before it can be merged and the Continuous Integration should be green. The title of a PR should contain a brief description. If it fixes an issue, in addition to the number of the issue, the title should also contain a description @@ -59,8 +59,7 @@ things like `echo "done"`. Don't use `unittest.suite` and `unittest.test`. Sample test: -.. code-block:: nim - + ```nim block: # foo doAssert foo(1) == 10 @@ -76,6 +75,7 @@ Sample test: @[false, false], @[false, false]] # doAssert with `not` can now be done as follows: doAssert not (1 == 2) + ``` Always refer to a GitHub issue using the following exact syntax: ``bug #1234`` as shown above, so that it's consistent and easier to search or for tooling. Some browser @@ -110,8 +110,7 @@ For a full spec, see here: ``testament/specs.nim`` An example of a test: -.. code-block:: nim - + ```nim discard """ errormsg: "type mismatch: got (PTest)" """ @@ -123,6 +122,7 @@ An example of a test: var buf: PTest buf.test() + ``` Running tests @@ -130,9 +130,9 @@ Running tests You can run the tests with -.. code-block:: cmd - + ```cmd ./koch tests + ``` which will run a good subset of tests. Some tests may fail. If you only want to see the output of failing tests, go for @@ -145,20 +145,20 @@ 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 ``testament/categories.nim``, at the bottom. -.. code:: cmd - + ```cmd ./koch tests c lib # compiles / runs stdlib modules, including `isMainModule` tests ./koch tests c megatest # runs a set of tests that can be combined into 1 + ``` To run a single test: -.. code:: cmd - + ```cmd ./koch test run / # e.g.: tuples/ttuples_issues ./koch test run tests/stdlib/tos.nim # can also provide relative path + ``` For reproducible tests (to reproduce an environment more similar to the one -run by Continuous Integration on github actions/azure pipelines), you may want to disable your +run by Continuous Integration on GitHub actions/azure pipelines), you may want to disable your local configuration (e.g. in ``~/.config/nim/nim.cfg``) which may affect some tests; this can also be achieved by using `export XDG_CONFIG_HOME=pathtoAlternateConfig`:cmd: before running `./koch`:cmd: @@ -174,35 +174,34 @@ The tester can compare two test runs. First, you need to create a reference test. You'll also need to the commit id, because that's what the tester needs to know in order to compare the two. -.. code:: cmd - + ```cmd git checkout devel DEVEL_COMMIT=$(git rev-parse HEAD) ./koch tests + ``` Then switch over to your changes and run the tester again. -.. code:: cmd - + ```cmd git checkout your-changes ./koch tests + ``` Then you can ask the tester to create a ``testresults.html`` which will tell you if any new tests passed/failed. -.. code:: cmd - + ```cmd ./koch tests --print html $DEVEL_COMMIT + ``` Deprecation =========== -Backward compatibility is important, so instead of a rename you need to deprecate -the old name and introduce a new name: - -.. code-block:: nim +Backwards compatibility is important. When renaming types, procedures, etc. the old name +must be marked as deprecated using the `deprecated` pragma: + ```nim # for routines (proc/template/macro/iterator) and types: proc oldProc(a: int, b: float): bool {.deprecated: "deprecated since v1.2.3; use `newImpl: string -> int` instead".} = discard @@ -214,9 +213,10 @@ the old name and introduce a new name: # (likewise with object types and their fields): type Bar {.deprecated.} = enum bar0, bar1 type Barz = enum baz0, baz1 {.deprecated.}, baz2 + ``` -See also `Deprecated `_ +See also [Deprecated](manual.html#pragmas-deprecated-pragma) pragma in the manual. @@ -234,70 +234,74 @@ test cases (typically 1 to 3 `assert` statements, depending on complexity). These `runnableExamples` are automatically run by `nim doc mymodule.nim`:cmd: as well as `testament`:cmd: and guarantee they stay in sync. -.. code-block:: nim + ```nim proc addBar*(a: string): string = ## Adds "Bar" to `a`. runnableExamples: assert "baz".addBar == "bazBar" result = a & "Bar" + ``` -See `parentDir `_ example. +See [parentDir](os.html#parentDir,string) example. The RestructuredText Nim uses has a special syntax for including code snippets embedded in documentation; these are not run by `nim doc`:cmd: and therefore are not guaranteed to stay in sync, so `runnableExamples` is almost always preferred: -.. code-block:: nim - + ````nim proc someProc*(): string = ## Returns "something" ## - ## .. code-block:: - ## echo someProc() # "something" + ## ``` + ## echo someProc() # "something" + ## ``` result = "something" # single-hash comments do not produce documentation + ```` -The ``.. code-block:: nim`` followed by a newline and an indentation instructs the +The \`\`\` followed by a newline and an indentation instructs the `nim doc`:cmd: command to produce syntax-highlighted example code with the -documentation (``.. code-block::`` is sufficient from inside a nim module). +documentation (\`\`\` is sufficient inside a ``.nim`` module, while from +a ``.md`` one needs to set the language explicitly as \`\`\`nim). When forward declaration is used, the documentation should be included with the first appearance of the proc. -.. code-block:: nim - + ```nim proc hello*(): string ## Put documentation here proc nothing() = discard proc hello*(): string = ## ignore this echo "hello" + ``` The preferred documentation style is to begin with a capital letter and use the third-person singular. That is, between: -.. code-block:: nim - + ```nim proc hello*(): string = ## Returns "hello" result = "hello" + ``` or -.. code-block:: nim - + ```nim proc hello*(): string = ## say hello result = "hello" + ``` the first is preferred. When you specify an *RST role* (highlighting/interpretation marker) do it in the postfix form for uniformity, that is after \`text in backticks\`. For example an ``:idx:`` role for referencing a topic ("SQLite" in the -example below) from `Nim Index`_ can be used in doc comment this way: +example below) from [Nim Index] can be used in doc comment this way: -.. code-block:: nim + ```nim ## A higher level `SQLite`:idx: database wrapper. + ``` .. _`Nim Index`: https://nim-lang.org/docs/theindex.html @@ -306,7 +310,7 @@ Inline monospaced text can be input using \`single backticks\` or the latter are not. To avoid accidental highlighting follow this rule in ``*.nim`` files: -* use single backticks for fragments of code in Nim and other +* Use single backticks for fragments of code in Nim and other programming languages, including identifiers, in ``*.nim`` files. For languages other than Nim add a role after final backtick, @@ -322,17 +326,17 @@ To avoid accidental highlighting follow this rule in ``*.nim`` files: Highlight shell commands by ``:cmd:`` role; for command line options use ``:option:`` role, e.g.: \`--docInternal\`:option:. -* prefer double backticks otherwise: +* Use double backticks: - * for file names: \`\`os.nim\`\` - * for fragments of strings **not** enclosed by `"` and `"` and not + * For file names: \`\`os.nim\`\` + * For fragments of strings **not** enclosed by `"` and `"` and not related to code, e.g. text of compiler messages - * also when code ends with a standalone ``\`` (otherwise a combination of + * When code ends with a standalone ``\`` (otherwise a combination of ``\`` and a final \` would get escaped) .. Note:: ``*.rst`` files have ``:literal:`` as their default role. So for them the rule above is only applicable if the ``:nim:`` role - is set up manually as the default [*]_:: + is set up manually as the default \[*]:: .. role:: nim(code) :language: nim @@ -341,7 +345,7 @@ To avoid accidental highlighting follow this rule in ``*.nim`` files: The first 2 lines are for other RST implementations, including Github one. - .. [*] this is fulfilled when ``doc/rstcommon.rst`` is included. + \[*] this is fulfilled when ``doc/rstcommon.rst`` is included. Best practices ============== @@ -355,50 +359,50 @@ New `defined(foo)` symbols need to be prefixed by the nimble package name, or by `nim` for symbols in nim sources (e.g. compiler, standard library). This is to avoid name conflicts across packages. -.. code-block:: nim - + ```nim # if in nim sources when defined(allocStats): discard # bad, can cause conflicts when defined(nimAllocStats): discard # preferred # if in a package `cligen`: when defined(debug): discard # bad, can cause conflicts when defined(cligenDebug): discard # preferred + ``` .. _noimplicitbool: Take advantage of no implicit bool conversion -.. code-block:: nim - + ```nim doAssert isValid() == true doAssert isValid() # preferred + ``` .. _design_for_mcs: Design with method call syntax chaining in mind -.. code-block:: nim - + ```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 - + ```nim quit() # bad in almost all cases doAssert() # preferred + ``` .. _tests_use_doAssert: Use `doAssert` (or `unittest.check`, `unittest.require`), not `assert` in all -tests so they'll be enabled even with `--assertions:off`:option:. - -.. code-block:: nim +tests, so they'll be enabled even with `--assertions:off`:option:. + ```nim block: # foo assert foo() # bad doAssert foo() # preferred + ``` .. _runnableExamples_use_assert: An exception to the above rule is `runnableExamples` and ``code-block`` rst blocks @@ -407,33 +411,33 @@ instead of `doAssert`. Note that `nim doc -d:danger main`:cmd: won't pass `-d:da `runnableExamples`, but `nim doc --doccmd:-d:danger main`:cmd: would, and so would the second example below: -.. code-block:: nim - + ```nim runnableExamples: doAssert foo() # bad assert foo() # preferred runnableExamples("-d:danger"): doAssert foo() # `assert` would be disabled here, so `doAssert` makes more sense + ``` .. _delegate_printing: Delegate printing to caller: return `string` instead of calling `echo` rationale: it's more flexible (e.g. allows the caller to call custom printing, -including prepending location info, writing to log files, etc). - -.. code-block:: nim +including prepending location info, writing to log files, etc.). + ```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, +(Ongoing debate) Consider using Option instead of return bool + var argument, unless stack allocation is needed (e.g. for efficiency). -.. code-block:: nim - + ```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`, @@ -441,10 +445,10 @@ except when that's not possible. It's more precise, easier for readers and maintainers 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 - + ```nim echo foo() # adds a line for testament in `output:` block inside `discard`. doAssert foo() == [1, 2] # preferred, except when not possible to do so. + ``` The `git`:cmd: stuff @@ -462,8 +466,8 @@ General commit rules 2. If you introduce changes which affect backward compatibility, make breaking changes, or have PR which is tagged as ``[feature]``, - the changes should be mentioned in `the changelog - `_. + the changes should be mentioned in [the changelog]( + https://github.com/nim-lang/Nim/blob/devel/changelog.md). 3. All changes introduced by the commit (diff lines) must be related to the subject of the commit. @@ -472,7 +476,7 @@ General commit rules your editor reformatted automatically the code or whatever different reason, this should be excluded from the commit. - *Tip:* Never commit everything as is using `git commit -a`:cmd:, but review + *Tip:* Never commit everything as-is using `git commit -a`:cmd:, but review carefully your changes with `git add -p`:cmd:. 4. Changes should not introduce any trailing whitespace. @@ -480,26 +484,26 @@ General commit rules Always check your changes for whitespace errors using `git diff --check`:cmd: or add the following ``pre-commit`` hook: - .. code:: cmd - - #!/bin/sh - git diff --check --cached || exit $? + ```cmd + #!/bin/sh + git diff --check --cached || exit $? + ``` 5. Describe your commit and use your common sense. Example commit message:: 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 + close it when the PR is committed), whereas issue ``#124`` is referenced (e.g.: partially fixed) and won't close the issue when committed. -6. PR body (not just PR title) should contain references to fixed/referenced github - issues, e.g.: ``fix #123`` or ``refs #123``. This is so that you get proper cross - referencing from linked issue to the PR (github won't make those links with just - PR title, and commit messages aren't always sufficient to ensure that, e.g. - can't be changed after a PR is merged). +6. PR body (not just PR title) should contain references to fixed/referenced GitHub + issues, e.g.: ``fix #123`` or ``refs #123``. This is so that you get proper + cross-referencing from linked issue to the PR (GitHub won't make those links + with just a PR title, and commit messages aren't always sufficient to ensure + that, e.g. can't be changed after a PR is merged). -7. Commits should be always be rebased against devel (so a fast forward +7. Commits should be always be rebased against devel (so a fast-forward merge can happen) e.g.: use `git pull --rebase origin devel`:cmd:. This is to avoid messing up @@ -519,14 +523,16 @@ 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 (e.g. for WIP or documentation only changes), add ``[skip ci]`` to your commit message title. - This convention is supported by our github actions pipelines and our azure pipeline + This convention is supported by our GitHub actions pipelines and our azure pipeline (using custom logic, which should complete in < 1mn) as well as our former other pipelines: - `Appveyor `_ - and `Travis `_. + [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 (azure, GitHub actions and builds.sr.ht) in your own Nim fork, and waiting for CI to be green in that fork (fixing bugs as needed) before - opening your PR in the original Nim repo, so as to reduce CI congestion. Same + opening your PR in the original Nim repo, 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. @@ -539,14 +545,16 @@ Debugging CI failures, flaky tests, etc 2. If CI failure seems unrelated to your PR, it could be caused by a flaky test. File a bug for it if it isn't already reported. A PR push (or opening/closing PR) will re-trigger all CI jobs (even successful ones, which can be wasteful). Instead, + request collaboration from the Nim team. The Nim team should follow these instructions to only restart the jobs that failed: * Azure: if on your own fork, it's possible from inside azure console - (e.g. ``dev.azure.com/username/username/_build/results?buildId=1430&view=results``) via ``rerun failed jobs`` on top. + (e.g. ``dev.azure.com/username/username/_build/results?buildId=1430&view=results``) via + ``rerun failed jobs`` on top. If either on you own fork or in Nim repo, it's possible from inside GitHub UI under checks tab, see https://github.com/timotheecour/Nim/issues/211#issuecomment-629751569 * GitHub actions: under "Checks" tab, click "Re-run jobs" in the right. - * builds.sr.ht: create a sourcehut account so you can restart a PR job as illustrated. + * builds.sr.ht: create a SourceHut account so that you can restart a PR job as illustrated. builds.sr.ht also allows you to ssh to a CI machine which can help a lot for debugging issues, see docs in https://man.sr.ht/builds.sr.ht/build-ssh.md and https://drewdevault.com/2019/08/19/Introducing-shell-access-for-builds.html; see @@ -561,13 +569,14 @@ Code reviews https://forum.nim-lang.org/t/4317 2. When reviewing large diffs that may involve code moving around, GitHub's interface - doesn't help much as it doesn't highlight moves. Instead, you can use something - like this, see visual results `here `_: - - .. code:: cmd + doesn't help much, as it doesn't highlight moves. Instead, you can use something + like this, see visual results [here]( + https://github.com/nim-lang/Nim/pull/10431#issuecomment-456968196): - git fetch origin pull/10431/head && git checkout FETCH_HEAD - git diff --color-moved-ws=allow-indentation-change --color-moved=blocks HEAD^ + ```cmd + git fetch origin pull/10431/head && git checkout FETCH_HEAD + git diff --color-moved-ws=allow-indentation-change --color-moved=blocks HEAD^ + ``` 3. In addition, you can view GitHub-like diffs locally to identify what was changed within a code block using `diff-highlight`:cmd: or `diff-so-fancy`:cmd:, e.g.: @@ -580,7 +589,7 @@ Code reviews -.. include:: docstyle.rst +.. include:: docstyle.md Evolving the stdlib @@ -614,7 +623,7 @@ Time handling, especially the `Time` type are also covered by this rule. Existing, battle-tested modules stay ------------------------------------ -Reason: There is no benefit in moving them around just to fullfill some design +Reason: There is no benefit in moving them around just to fulfill some design fashion as in "Nim's core MUST BE SMALL". If you don't like an existing module, don't import it. If a compilation target (e.g. JS) cannot support a module, document this limitation. @@ -628,7 +637,7 @@ Syntactic helpers can start as experimental stdlib modules Reason: Generally speaking as external dependencies they are not exposed to enough users so that we can see if the shortcuts provide enough benefit -or not. Many programmers avoid external dependencies, even moreso for +or not. Many programmers avoid external dependencies, especially for "tiny syntactic improvements". However, this is only true for really good syntactic improvements that have the potential to clean up other parts of the Nim library substantially. If in doubt, new stdlib modules should start @@ -653,7 +662,7 @@ to existing modules is acceptable. For two reasons: ("Why does sequtils lack a `countIt`? Because version 1.0 happens to have lacked it? Silly...") 2. To encourage contributions. Contributors often start with PRs that - add simple things and then they stay and also fix bugs. Nim is an + add simple things, then they stay and also fix bugs. Nim is an open source project and lives from people's contributions and involvement. Newly introduced issues have to be balanced against motivating new people. We know where to find perfectly designed pieces of software that have no bugs -- these are the systems @@ -679,7 +688,7 @@ Conventions Breaking Changes ================ -Introducing breaking changes, no matter how well intentioned, +Introducing breaking changes, no matter how well-intentioned, creates long-term problems for the community, in particular those looking to promote reusable Nim code in libraries: In the Nim distribution, critical security and bugfixes, language changes and community improvements are bundled in a single distribution - it is @@ -714,14 +723,14 @@ Examples of run-time breaking changes: * "Nim's path handling procs like `getXDir` now consistently lack the trailing slash" * "Nim's strformat implementation is now more consistent with Python" -Instead write new code that explicitly announces the feature you think we announced but +Instead, write new code that explicitly announces the feature you think we announced but didn't. For example, `strformat` does not say "it's compatible with Python", it says "inspired by Python's f-strings". This new code can be submitted to the stdlib -and the old code can be deprecated or it can be published as a Nimble package. +and the old code can be deprecated or published as a Nimble package. Sometimes, a run-time breaking change is most desirable: For example, a string representation of a floating point number that "roundtrips" is much better than -a string represenation that doesn't. These run-time breaking changes must start in the +a string representation that doesn't. These run-time breaking changes must start in the state "opt-in" via a new `-d:nimPreviewX` or command line flag and then should become the new default later, in follow-up versions. This way users can track regressions more easily. ("git bisect" is not an acceptable alternative, that's for @@ -735,7 +744,8 @@ Compile-time breaking changes ----------------------------- Compile-time breaking changes are usually easier to handle, but for large code bases -it can also be much work and it can hinder the adoption of a new Nim release. +they can also involve a large amount of work and can hinder the adoption of a new +Nim release. Additive approaches are to be preferred here as well. Examples of compile-time breaking changes include (but are not limited to): @@ -743,10 +753,10 @@ Examples of compile-time breaking changes include (but are not limited to): * Renaming functions and modules, or moving things. Instead of a direct rename, deprecate the old name and introduce a new one. * Renaming the parameter names: Thanks to Nim's "named parameter" calling syntax - like `f(x = 0, y = 1)` this is a breaking change. Instead live with the existing + like `f(x = 0, y = 1)` this is a breaking change. Instead, live with the existing parameter names. * Adding an enum value to an existing enum. Nim's exhaustive case statements stop - compiling after such a change. Instead consider to introduce new `bool` + compiling after such a change. Instead, consider to introduce new `bool` fields/parameters. This can be impractical though, so we use good judgement and our list of "important packages" to see if it doesn't break too much code out there in practice. diff --git a/doc/destructors.rst b/doc/destructors.md similarity index 97% rename from doc/destructors.rst rename to doc/destructors.md index c98e94536a41..b0dd42597937 100644 --- a/doc/destructors.rst +++ b/doc/destructors.md @@ -30,8 +30,7 @@ Motivating example With the language mechanisms described here, a custom seq could be written as: -.. code-block:: nim - + ```nim type myseq*[T] = object len, cap: int @@ -91,7 +90,7 @@ written as: for i in 0..`_. +"Lifetime-tracking hooks", which are particular [type bound operators]( +manual.html#procedures-type-bound-operators). There are 4 different hooks for each (generic or concrete) object type `T` (`T` can also be a `distinct` type) that are called implicitly by the compiler. @@ -119,20 +119,18 @@ to return. The prototype of this hook for a type `T` needs to be: -.. code-block:: nim - + ```nim proc `=destroy`(x: var T) - + ``` The general pattern in `=destroy` looks like: -.. code-block:: nim - + ```nim proc `=destroy`(x: var T) = # first check if 'x' was moved to somewhere else: if x.field != nil: freeResource(x.field) - + ``` `=sink` hook @@ -149,20 +147,19 @@ provide `=destroy` and `=copy`, the compiler will take care of the rest. The prototype of this hook for a type `T` needs to be: -.. code-block:: nim - + ```nim proc `=sink`(dest: var T; source: T) - + ``` The general pattern in `=sink` looks like: -.. code-block:: nim + ```nim proc `=sink`(dest: var T; source: T) = `=destroy`(dest) wasMoved(dest) dest.field = source.field - + ``` **Note**: `=sink` does not need to check for self-assignments. How self-assignments are handled is explained later in this document. @@ -177,29 +174,27 @@ operations. The prototype of this hook for a type `T` needs to be: -.. code-block:: nim - + ```nim proc `=copy`(dest: var T; source: T) - + ``` The general pattern in `=copy` looks like: -.. code-block:: nim - + ```nim proc `=copy`(dest: var T; source: T) = # protect against self-assignments: if dest.field != source.field: `=destroy`(dest) wasMoved(dest) dest.field = duplicateResource(source.field) - + ``` The `=copy` proc can be marked with the `{.error.}` pragma. Then any assignment that otherwise would lead to a copy is prevented at compile-time. This looks like: -.. code-block:: nim - + ```nim proc `=copy`(dest: var T; source: T) {.error.} + ``` but a custom error message (e.g., `{.error: "custom error".}`) will not be emitted by the compiler. Notice that there is no `=` before the `{.error.}` pragma. @@ -215,9 +210,9 @@ memory or resources, but memory safety is not compromised. The prototype of this hook for a type `T` needs to be: -.. code-block:: nim - + ```nim proc `=trace`(dest: var T; env: pointer) + ``` `env` is used by ORC to keep track of its internal state, it should be passed around to calls of the built-in `=trace` operation. @@ -233,8 +228,7 @@ prevent the automatic creation. The general pattern in using `=destroy` with `=trace` looks like: -.. code-block:: nim - + ```nim type Test[T] = object size: Natural @@ -255,6 +249,7 @@ The general pattern in using `=destroy` with `=trace` looks like: for i in 0 ..< dest.size: `=trace`(dest.arr[i], env) # following may be other custom "hooks" as required... + ``` **Note**: The `=trace` hooks (which are only used by `--mm:orc`) are currently more experimental and less refined than the other hooks. @@ -307,8 +302,7 @@ not a linear type system. The employed static analysis is limited and only concerned with local variables; however, object and tuple fields are treated as separate entities: -.. code-block:: nim - + ```nim proc consume(x: sink Obj) = discard "no implementation" proc main = @@ -316,16 +310,16 @@ however, object and tuple fields are treated as separate entities: consume tup[0] # ok, only tup[0] was consumed, tup[1] is still alive: echo tup[1] - + ``` Sometimes it is required to explicitly `move` a value into its final position: -.. code-block:: nim - + ```nim proc main = var dest, src: array[10, string] # ... for i in 0..high(dest): dest[i] = move(src[i]) + ``` An implementation is allowed, but not required to implement even more move optimizations (and the current implementation does not). @@ -344,11 +338,10 @@ use `{.push sinkInference: on.}` ... `{.pop.}`. The `.nosinks`:idx: pragma can be used to disable this inference for a single routine: -.. code-block:: nim - + ```nim proc addX(x: T; child: T) {.nosinks.} = x.s.add child - + ``` The details of the inference algorithm are currently undocumented. @@ -456,8 +449,7 @@ The complex case looks like a variant of `x = f(x)`, we consider `x = select(rand() < 0.5, x, y)` here: -.. code-block:: nim - + ```nim proc select(cond: bool; a, b: sink string): string = if cond: result = a # moves a into result @@ -469,13 +461,11 @@ The complex case looks like a variant of `x = f(x)`, we consider var y = "xyz" # possible self-assignment: x = select(true, x, y) - + ``` Is transformed into: - -.. code-block:: nim - + ```nim proc select(cond: bool; a, b: sink string): string = try: if cond: @@ -506,6 +496,7 @@ Is transformed into: finally: `=destroy`(y) `=destroy`(x) + ``` As can be manually verified, this transformation is correct for self-assignments. @@ -527,8 +518,7 @@ that the pointer does not outlive its origin. No destructor call is injected for expressions of type `lent T` or of type `var T`. -.. code-block:: nim - + ```nim type Tree = object kids: seq[Tree] @@ -553,6 +543,7 @@ for expressions of type `lent T` or of type `var T`. # everything turned into moves: let t = construct(@[construct(@[]), construct(@[])]) echo t[0] # accessor does not copy the element! + ``` The cursor pragma @@ -564,12 +555,12 @@ This means that cyclic structures cannot be freed immediately (`--mm:orc`:option: ships with a cycle collector). With the `cursor` pragma one can break up cycles declaratively: -.. code-block:: nim - + ```nim type Node = ref object left: Node # owning ref right {.cursor.}: Node # non-owning ref + ``` But please notice that this is not C++'s weak_ptr, it means the right field is not involved in the reference counting, it is a raw pointer without runtime checks. @@ -578,13 +569,12 @@ Automatic reference counting also has the disadvantage that it introduces overhe when iterating over linked structures. The `cursor` pragma can also be used to avoid this overhead: -.. code-block:: nim - + ```nim var it {.cursor.} = listRoot while it != nil: use(it) it = it.next - + ``` In fact, `cursor` more generally prevents object construction/destruction pairs and so can also be useful in other contexts. The alternative solution would be to @@ -609,13 +599,13 @@ words, we do a compile-time copy-on-write analysis. This means that "borrowed" views can be written naturally and without explicit pointer indirections: -.. code-block:: nim - + ```nim proc main(tab: Table[string, string]) = let v = tab["key"] # inferred as cursor because 'tab' is not mutated. # no copy into 'v', no destruction of 'v'. use(v) useItAgain(v) + ``` Hook lifting @@ -639,8 +629,7 @@ Hook generation The ability to override a hook leads to a phase ordering problem: -.. code-block:: nim - + ```nim type Foo[T] = object @@ -651,7 +640,7 @@ The ability to override a hook leads to a phase ordering problem: proc `=destroy`[T](f: var Foo[T]) = discard - + ``` The solution is to define ``proc `=destroy`[T](f: var Foo[T])`` before it is used. The compiler generates implicit @@ -674,8 +663,7 @@ The experimental `nodestroy`:idx: pragma inhibits hook injections. This can be used to specialize the object traversal in order to avoid deep recursions: -.. code-block:: nim - + ```nim type Node = ref object x, y: int32 left, right: Node @@ -695,6 +683,7 @@ used to specialize the object traversal in order to avoid deep recursions: # notice how even the destructor for 's' is not called implicitly # anymore thanks to .nodestroy, so we have to call it on our own: `=destroy`(s) + ``` As can be seen from the example, this solution is hardly sufficient and @@ -712,19 +701,20 @@ The copy operation is deferred until the first write. For example: -.. code-block:: nim + ```nim var x = "abc" # no copy var y = x # no copy y[0] = 'h' # copy - + ``` The abstraction fails for `addr x` because whether the address is going to be used for mutations is unknown. `prepareMutation` needs to be called before the "address of" operation. For example: -.. code-block:: nim + ```nim var x = "abc" var y = x prepareMutation(y) moveMem(addr y[0], addr x[0], 3) assert y == "abc" + ``` diff --git a/doc/docgen.rst b/doc/docgen.md similarity index 85% rename from doc/docgen.rst rename to doc/docgen.md index 48166b2c5c89..27530737a79b 100644 --- a/doc/docgen.rst +++ b/doc/docgen.md @@ -14,35 +14,24 @@ Introduction ============ This document describes the `documentation generation tools`:idx: built into -the `Nim compiler `_, which can generate HTML, Latex and JSON output +the [Nim compiler](nimc.html), which can generate HTML, Latex and JSON output from input ``.nim`` files and projects, as well as HTML and LaTeX from input RST (reStructuredText) files. The output documentation will include the module dependencies (`import`), any top-level documentation comments (`##`), and exported symbols (`*`), including procedures, types, and variables. -=================== ====================== ============ ============== -command runs on... input format output format -=================== ====================== ============ ============== -`nim doc`:cmd: documentation comments ``.nim`` ``.html`` HTML -`nim doc2tex`:cmd: ″ ″ ``.tex`` LaTeX -`nim jsondoc`:cmd: ″ ″ ``.json`` JSON -`nim rst2html`:cmd: standalone rst files ``.rst`` ``.html`` HTML -`nim rst2tex`:cmd: ″ ″ ``.tex`` LaTeX -=================== ====================== ============ ============== - Quick start ----------- Generate HTML documentation for a file: -.. code:: cmd - + ```cmd nim doc .nim + ``` Generate HTML documentation for a whole project: -.. code:: cmd - + ```cmd # delete any htmldocs/*.idx file before starting nim doc --project --index:on --git.url: --git.commit: --outdir:htmldocs .nim # this will generate html files, a theindex.html index, css and js under `htmldocs` @@ -54,25 +43,26 @@ Generate HTML documentation for a whole project: # or `$nimcache/htmldocs` with `--usenimcache` which avoids clobbering your sources; # and likewise without `--project`. # Adding `-r` will open in a browser directly. - + ``` Documentation Comments ---------------------- Any comments which are preceded by a double-hash (`##`), are interpreted as -documentation. Comments are parsed as RST (see `reference -`_), providing +documentation. Comments are parsed as RST (see [reference]( +http://docutils.sourceforge.net/docs/user/rst/quickref.html)), providing Nim module authors the ability to easily generate richly formatted documentation with only their well-documented code! Basic Markdown syntax is also supported inside the doc comments. Example: -.. code-block:: nim + ```nim type Person* = object ## This type contains a description of a person name: string age: int + ``` Outputs:: Person* = object @@ -83,9 +73,10 @@ Outputs:: Field documentation comments can be added to fields like so: -.. code-block:: nim + ```nim var numValues: int ## \ ## `numValues` stores the number of values + ``` Note that without the `*` following the name of the type, the documentation for this type would not be generated. Documentation will only be generated for @@ -103,12 +94,13 @@ won't influence RST formatting. If you still need to add an additional indentation at the very beginning (for RST block quote syntax) use backslash \\ before it: - .. code-block:: nim - ## \ - ## - ## Block quote at the first line. - ## - ## Paragraph. + ```nim + ## \ + ## + ## Block quote at the first line. + ## + ## Paragraph. + ``` Document Types @@ -120,8 +112,8 @@ Example of Nim file input The following examples will generate documentation for this sample *Nim* module, aptly named ``doc/docgen_sample.nim``: -.. code:: nim - :file: docgen_sample.nim + ```nim file=docgen_sample.nim + ``` All the below commands save their output to ``htmldocs`` directory relative to the directory of file; @@ -137,16 +129,16 @@ optionally, an index file. The `doc`:option: command: -.. code:: cmd - + ```cmd nim doc docgen_sample.nim + ``` Partial Output:: ... proc helloWorld(times: int) {.raises: [], tags: [].} ... -The full output can be seen here: `docgen_sample.html `_. +The full output can be seen here: [docgen_sample.html](docgen_sample.html). It runs after semantic checking and includes pragmas attached implicitly by the compiler. @@ -159,8 +151,7 @@ HTML -> PDF conversion). The `doc2tex`:option: command: -.. code:: cmd - + ```cmd nim doc2tex docgen_sample.nim cd htmldocs xelatex docgen_sample.tex @@ -169,6 +160,7 @@ The `doc2tex`:option: command: # large documents) to get all labels generated. # That depends on this warning in the end of `xelatex` output: # LaTeX Warning: Label(s) may have changed. Rerun to get cross-references right. + ``` The output is ``docgen_sample.pdf``. @@ -183,9 +175,9 @@ Note that this tool is built off of the `doc`:option: command The `jsondoc`:option: command: -.. code:: cmd - + ```cmd nim jsondoc docgen_sample.nim + ``` Output:: { @@ -209,9 +201,9 @@ renamed to `jsondoc0`:option:. The `jsondoc0`:option: command: -.. code:: cmd - + ```cmd nim jsondoc0 docgen_sample.nim + ``` Output:: [ @@ -243,15 +235,15 @@ underscore `_` to a *link text*. Link text is either one word or a group of words enclosed by backticks `\`` (for a one word case backticks are usually omitted). Link text will be displayed *as is* while *link target* will be set to -the anchor [*]_ of Nim symbol that corresponds to link text. +the anchor \[*] of Nim symbol that corresponds to link text. -.. [*] anchors' format is described in `HTML anchor generation`_ section below. +\[*] anchors' format is described in [HTML anchor generation] section below. If you have a constant: -.. code:: Nim - - const pi* = 3.14 + ```Nim + const pi* = 3.14 + ``` then it should be referenced in one of the 2 forms: @@ -262,9 +254,9 @@ B. qualified (with symbol kind specification):: For routine kinds there are more options. Consider this definition: -.. code:: Nim - - proc foo*(a: int, b: float): string + ```Nim + proc foo*(a: int, b: float): string + ``` Generally following syntax is allowed for referencing `foo`: @@ -352,11 +344,11 @@ recognized fine:: (without parameter names, see form A.2 above). E.g. for this signature: - .. code:: Nim - + ```Nim proc binarySearch*[T, K](a: openArray[T]; key: K; cmp: proc (x: T; y: K): int {.closure.}): int ~~ ~~ ~~~~~ + ``` you cannot use names underlined by `~~` so it must be referenced with ``cmp: proc(T, K)``. Hence these forms are valid:: @@ -379,10 +371,10 @@ recognized fine:: .. Note:: A bit special case is operators (as their signature is also defined with `\``): - .. code:: Nim - + ```Nim func `$`(x: MyType): string func `[]`*[T](x: openArray[T]): T + ``` A short form works without additional backticks:: @@ -412,9 +404,9 @@ Related Options Project switch -------------- -.. code:: cmd - + ```cmd nim doc --project filename.nim + ``` This will recursively generate documentation of all Nim modules imported into the input module that belong to the Nimble package that ``filename.nim`` @@ -425,27 +417,27 @@ also be generated. Index switch ------------ -.. code:: cmd - + ```cmd nim doc --index:on filename.nim + ``` This will generate an index of all the exported symbols in the input Nim module, and put it into a neighboring file with the extension of ``.idx``. The index file is line-oriented (newlines have to be escaped). Each line represents a tab-separated record of several columns, the first two mandatory, -the rest optional. See the `Index (idx) file format`_ section for details. +the rest optional. See the [Index (idx) file format] section for details. Once index files have been generated for one or more modules, the Nim compiler command `buildIndex directory` can be run to go over all the index -files in the specified directory to generate a `theindex.html `_ +files in the specified directory to generate a [theindex.html](theindex.html) file. See source switch ----------------- -.. code:: cmd - + ```cmd nim doc --git.url: filename.nim + ``` With the `git.url`:option: switch the *See source* hyperlink will appear below each documented item in your source code pointing to the implementation of that @@ -471,11 +463,7 @@ You can edit ``config/nimdoc.cfg`` and modify the ``doc.item.seesrc`` value with a hyperlink to your own code repository. In the case of Nim's own documentation, the `commit` value is just a commit -hash to append to a formatted URL to https://github.com/nim-lang/Nim. The -``tools/nimweb.nim`` helper queries the current git commit hash during the doc -generation, but since you might be working on an unpublished repository, it -also allows specifying a `githash` value in ``web/website.ini`` to force a -specific commit in the output. +hash to append to a formatted URL to https://github.com/nim-lang/Nim. Other Input Formats @@ -486,20 +474,10 @@ the `rst2html`:option: and `rst2tex`:option: commands. Documents like this one a initially written in a dialect of RST which adds support for Nim source code highlighting with the ``.. code-block:: nim`` prefix. ``code-block`` also supports highlighting of a few other languages supported by the -`packages/docutils/highlite module `_. - -Usage: - -.. code:: cmd - - nim rst2html docgen.rst - -Output:: - You're reading it! - -The `rst2tex`:option: command is invoked identically to `rst2html`:option:, -but outputs a ``.tex`` file instead of ``.html``. +[packages/docutils/highlite module](highlite.html). +See [Markdown and RST markup languages](markdown_rst.html) for +usage of those commands. HTML anchor generation ====================== @@ -528,7 +506,7 @@ HTML file, most browsers will go to the first one. To differentiate the rest, you will need to use the complex name. A complex name for a callable type is made up of several parts: - (**plain symbol**)(**.type**),(**first param**)?(**,param type**)\* + (**plain symbol**)(**.type**),(**first param**)?(**,param type**)\* The first thing to note is that all callable types have at least a comma, even if they don't have any parameters. If there are parameters, they are @@ -548,38 +526,38 @@ Callable type Suffix The relationship of type to suffix is made by the proc `complexName` in the ``compiler/docgen.nim`` file. Here are some examples of complex names for -symbols in the `system module `_. +symbols in the [system module](system.html). * `type SomeSignedInt = int | int8 | int16 | int32 | int64` **=>** - `#SomeSignedInt `_ + [#SomeSignedInt](system.html#SomeSignedInt) * `var globalRaiseHook: proc (e: ref E_Base): bool {.nimcall.}` **=>** - `#globalRaiseHook `_ + [#globalRaiseHook](system.html#globalRaiseHook) * `const NimVersion = "0.0.0"` **=>** - `#NimVersion `_ + [#NimVersion](system.html#NimVersion) * `proc getTotalMem(): int {.rtl, raises: [], tags: [].}` **=>** - `#getTotalMem, `_ + [#getTotalMem](system.html#getTotalMem) * `proc len[T](x: seq[T]): int {.magic: "LengthSeq", noSideEffect.}` **=>** - `#len,seq[T] `_ + [#len,seq[T]](system.html#len,seq[T]) * `iterator pairs[T](a: seq[T]): tuple[key: int, val: T] {.inline.}` **=>** - `#pairs.i,seq[T] `_ + [#pairs.i,seq[T]](iterators.html#pairs.i,seq[T]) * `template newException[](exceptn: typedesc; message: string; parentException: ref Exception = nil): untyped` **=>** - `#newException.t,typedesc,string,ref.Exception - `_ + [#newException.t,typedesc,string,ref.Exception]( + system.html#newException.t,typedesc,string,ref.Exception) Index (idx) file format ======================= -Files with the ``.idx`` extension are generated when you use the `Index -switch <#related-options-index-switch>`_ along with commands to generate +Files with the ``.idx`` extension are generated when you use the [Index +switch] along with commands to generate documentation from source or text files. You can programmatically generate -indices with the `setIndexTerm() -`_ +indices with the [setIndexTerm()]( +rstgen.html#setIndexTerm,RstGenerator,string,string,string,string,string) and `writeIndexFile() `_ procs. The purpose of `idx` files is to hold the interesting symbols and their HTML references so they can be later concatenated into a big index file with -`mergeIndexes() `_. This section documents +[mergeIndexes()](rstgen.html#mergeIndexes,string). This section documents the file format in detail. Index files are line-oriented and tab-separated (newline and tab characters @@ -624,16 +602,15 @@ final index, and TOC entries found in ``.nim`` files are discarded. Additional resources ==================== -* `Nim Compiler User Guide `_ - -* Documentation for `rst module `_ -- Nim RST/Markdown parser. +* [Nim Compiler User Guide](nimc.html#compiler-usage-commandminusline-switches) -* `RST Quick Reference - `_ +* already mentioned documentation for + [Markdown and RST markup languages](markdown_rst.html), which also + contains the list of implemented features of these markup languages. The output for HTML and LaTeX comes from the ``config/nimdoc.cfg`` and ``config/nimdoc.tex.cfg`` configuration files. You can add and modify these files to your project to change the look of the docgen output. -You can import the `packages/docutils/rstgen module `_ in your +You can import the [packages/docutils/rstgen module](rstgen.html) in your programs if you want to reuse the compiler's documentation generation procs. diff --git a/doc/docs.rst b/doc/docs.md similarity index 69% rename from doc/docs.rst rename to doc/docs.md index 3c348fcb83ce..b6ff6d2c70bc 100644 --- a/doc/docs.rst +++ b/doc/docs.md @@ -1,38 +1,38 @@ The documentation consists of several documents: -- | `Tutorial (part I) `_ +- | [Tutorial (part I)](tut1.html) | The Nim tutorial part one deals with the basics. -- | `Tutorial (part II) `_ +- | [Tutorial (part II)](tut2.html) | The Nim tutorial part two deals with the advanced language constructs. -- | `Tutorial (part III) `_ +- | [Tutorial (part III)](tut3.html) | The Nim tutorial part three about Nim's macro system. -- | `Language Manual `_ +- | [Language Manual](manual.html) | The Nim manual is a draft that will evolve into a proper specification. -- | `Library documentation `_ +- | [Library documentation](lib.html) | This document describes Nim's standard library. -- | `Compiler user guide `_ +- | [Compiler user guide](nimc.html) | The user guide lists command line arguments, special features of the compiler, etc. -- | `Tools documentation `_ +- | [Tools documentation](tools.html) | Description of some tools that come with the standard distribution. -- | `Memory management `_ +- | [Memory management](mm.html) | Additional documentation about Nim's memory management strategies | and how to operate them in a realtime setting. -- | `Source code filters `_ +- | [Source code filters](filters.html) | The Nim compiler supports source code filters as a simple yet powerful builtin templating system. -- | `Internal documentation `_ +- | [Internal documentation](intern.html) | The internal documentation describes how the compiler is implemented. Read this if you want to hack the compiler. -- | `Index `_ +- | [Index](theindex.html) | The generated index. **Index + (Ctrl+F) == Joy** diff --git a/doc/docstyle.rst b/doc/docstyle.md similarity index 89% rename from doc/docstyle.rst rename to doc/docstyle.md index df1f36dad842..291a34cf684a 100644 --- a/doc/docstyle.rst +++ b/doc/docstyle.md @@ -4,7 +4,7 @@ Documentation Style General Guidelines ------------------ -* See also `nep1`_ which should probably be merged here. +* See also [nep1](nep1.html) which should probably be merged here. * Authors should document anything that is exported; documentation for private procs can be useful too (visible via `nim doc --docInternal foo.nim`:cmd:). * Within documentation, a period (`.`) should follow each sentence (or sentence fragment) in a comment block. @@ -18,11 +18,11 @@ General Guidelines * (debatable) In nim sources, for links, prefer ``[link text](link.html)`` to `\`link text\`_`:code: since the syntax is simpler and markdown is more common (likewise, `nim rst2html`:cmd: also supports it in ``rst`` files). -.. code-block:: nim - + ```nim proc someproc*(s: string, foo: int) = ## Use single backticks for inline code, e.g.: `s` or `someExpr(true)`. ## Use a backlash to follow with alphanumeric char: `int8`\s are great. + ``` Module-level documentation @@ -32,23 +32,24 @@ Documentation of a module is placed at the top of the module itself. Each line o Sometimes `##[ multiline docs containing code ]##` is preferable, see ``lib/pure/times.nim``. Code samples are encouraged, and should follow the general RST syntax: -.. code-block:: Nim - + ````Nim ## The `universe` module computes the answer to life, the universe, and everything. ## - ## .. code-block:: - ## doAssert computeAnswerString() == 42 + ## ``` + ## doAssert computeAnswerString() == 42 + ## ``` + ```` Within this top-level comment, you can indicate the authorship and copyright of the code, which will be featured in the produced documentation. -.. code-block:: Nim - + ```Nim ## This is the best module ever. It provides answers to everything! ## ## :Author: Steve McQueen ## :Copyright: 1965 ## + ``` Leave a space between the last line of top-level documentation and the beginning of Nim code (the imports, etc.). @@ -57,28 +58,29 @@ Procs, Templates, Macros, Converters, and Iterators The documentation of a procedure should begin with a capital letter and should be in present tense. Variables referenced in the documentation should be surrounded by single tick marks: -.. code-block:: Nim - + ```Nim proc example1*(x: int) = ## Prints the value of `x`. echo x + ``` Whenever an example of usage would be helpful to the user, you should include one within the documentation in RST format as below. -.. code-block:: Nim - + ````Nim proc addThree*(x, y, z: int8): int = ## Adds three `int8` values, treating them as unsigned and ## truncating the result. ## - ## .. code-block:: - ## # things that aren't suitable for a `runnableExamples` go in code-block: - ## echo execCmdEx("git pull") - ## drawOnScreen() + ## ``` + ## # things that aren't suitable for a `runnableExamples` go in code block: + ## echo execCmdEx("git pull") + ## drawOnScreen() + ## ``` runnableExamples: - # `runnableExamples` is usually preferred to ``code-block``, when possible. + # `runnableExamples` is usually preferred to code blocks, when possible. doAssert addThree(3, 125, 6) == -122 result = x +% y +% z + ```` The command `nim doc`:cmd: will then correctly syntax highlight the Nim code within the documentation. @@ -87,8 +89,7 @@ Types Exported types should also be documented. This documentation can also contain code samples, but those are better placed with the functions to which they refer. -.. code-block:: Nim - + ```Nim type NamedQueue*[T] = object ## Provides a linked data structure with names ## throughout. It is named for convenience. I'm making @@ -96,12 +97,12 @@ Exported types should also be documented. This documentation can also contain co name*: string ## The name of the item val*: T ## Its value next*: ref NamedQueue[T] ## The next item in the queue + ``` You have some flexibility when placing the documentation: -.. code-block:: Nim - + ```Nim type NamedQueue*[T] = object ## Provides a linked data structure with names @@ -110,11 +111,11 @@ You have some flexibility when placing the documentation: name*: string ## The name of the item val*: T ## Its value next*: ref NamedQueue[T] ## The next item in the queue + ``` Make sure to place the documentation beside or within the object. -.. code-block:: Nim - + ```Nim type ## Bad: this documentation disappears because it annotates the `type` keyword ## above, not `NamedQueue`. @@ -123,14 +124,14 @@ Make sure to place the documentation beside or within the object. ## is not what we want. val*: T ## Its value next*: ref NamedQueue[T] ## The next item in the queue + ``` Var, Let, and Const ------------------- When declaring module-wide constants and values, documentation is encouraged. The placement of doc comments is similar to the `type` sections. -.. code-block:: Nim - + ```Nim const X* = 42 ## An awesome number. SpreadArray* = [ @@ -138,25 +139,26 @@ When declaring module-wide constants and values, documentation is encouraged. Th [2,3,1], [3,1,2], ] ## Doc comment for `SpreadArray`. + ``` Placement of comments in other areas is usually allowed, but will not become part of the documentation output and should therefore be prefaced by a single hash (`#`). -.. code-block:: Nim - + ```Nim const BadMathVals* = [ 3.14, # pi 2.72, # e 0.58, # gamma ] ## A bunch of badly rounded values. + ``` Nim supports Unicode in comments, so the above can be replaced with the following: -.. code-block:: Nim - + ```Nim const BadMathVals* = [ 3.14, # π 2.72, # e 0.58, # γ ] ## A bunch of badly rounded values (including π!). + ``` diff --git a/doc/drnim.rst b/doc/drnim.md similarity index 97% rename from doc/drnim.rst rename to doc/drnim.md index 070cd17871f9..48a63344682f 100644 --- a/doc/drnim.rst +++ b/doc/drnim.md @@ -14,7 +14,7 @@ Introduction ============ This document describes the usage of the *DrNim* tool. DrNim combines -the Nim frontend with the `Z3 `_ proof +the Nim frontend with the [Z3](https://github.com/Z3Prover/z3) proof engine, in order to allow verify/validate software written in Nim. DrNim's command-line options are the same as the Nim compiler's. @@ -22,11 +22,11 @@ DrNim's command-line options are the same as the Nim compiler's. DrNim currently only checks the sections of your code that are marked via `staticBoundChecks: on`: -.. code-block:: nim - + ```nim {.push staticBoundChecks: on.} # <--- code section here ----> {.pop.} + ``` DrNim currently only tries to prove array indexing or subrange checks, overflow errors are *not* prevented. Overflows will be checked for in @@ -53,8 +53,7 @@ Motivating Example The follow example highlights what DrNim can easily do, even without additional annotations: -.. code-block:: nim - + ```nim {.push staticBoundChecks: on.} proc sum(a: openArray[int]): int = @@ -64,6 +63,7 @@ without additional annotations: {.pop.} echo sum([1, 2, 3]) + ``` This program contains a famous "index out of bounds" bug. DrNim detects it and produces the following error message:: @@ -125,8 +125,7 @@ Example: insertionSort **Note**: This example does not yet work with DrNim. -.. code-block:: nim - + ```nim import std / logic proc insertionSort(a: var openArray[int]) {. @@ -142,6 +141,7 @@ Example: insertionSort {.invariant: forall(j in 1..k, i in 0..\n" & "\n") + ``` Each line that does not start with the meta character (ignoring leading diff --git a/doc/grammar.txt b/doc/grammar.txt index 1e41b9eeb156..878a4e5e1f2a 100644 --- a/doc/grammar.txt +++ b/doc/grammar.txt @@ -171,7 +171,7 @@ objectCase = 'case' identWithPragma ':' typeDesc ':'? COMMENT? | IND{=} objectBranches) objectPart = IND{>} objectPart^+IND{=} DED / objectWhen / objectCase / 'nil' / 'discard' / declColonEquals -objectDecl = 'object' pragma? ('of' typeDesc)? COMMENT? objectPart +objectDecl = 'object' ('of' typeDesc)? COMMENT? objectPart conceptParam = ('var' | 'out')? symbol conceptDecl = 'concept' conceptParam ^* ',' (pragma)? ('of' typeDesc ^* ',')? &IND{>} stmt diff --git a/doc/hcr.rst b/doc/hcr.md similarity index 97% rename from doc/hcr.rst rename to doc/hcr.md index dd25e39b37cb..285a862821de 100644 --- a/doc/hcr.rst +++ b/doc/hcr.md @@ -26,8 +26,7 @@ code when `F9` is pressed. The important lines are marked with `#***`. To install SDL2 you can use `nimble install sdl2`:cmd:. -.. code-block:: nim - + ```nim # logic.nim import sdl2 @@ -83,10 +82,10 @@ To install SDL2 you can use `nimble install sdl2`:cmd:. discard renderer.fillRect(rect) delay(16) renderer.present() + ``` -.. code-block:: nim - + ```nim # mymain.nim import logic @@ -97,42 +96,44 @@ To install SDL2 you can use `nimble install sdl2`:cmd:. destroy() main() + ``` Compile this example via: -```cmd + ```cmd nim c --hotcodereloading:on mymain.nim -``` + ``` Now start the program and KEEP it running! -.. code:: cmd + ```cmd # Unix: mymain & # or Windows (click on the .exe) mymain.exe # edit + ``` For example, change the line: -```nim + ```nim discard renderer.setDrawColor(255, 128, 128, 0) -``` + ``` into: -```nim + ```nim discard renderer.setDrawColor(255, 255, 128, 0) -``` + ``` (This will change the color of the rectangle.) Then recompile the project, but do not restart or quit the mymain.exe program! -```cmd + ```cmd nim c --hotcodereloading:on mymain.nim -``` + ``` Now give the `mymain` SDL window the focus, press F9, and watch the updated version of the program. @@ -146,7 +147,7 @@ One can use the special event handlers `beforeCodeReload` and `afterCodeReload` to reset the state of a particular variable or to force the execution of certain statements: -.. code-block:: Nim + ```Nim var settings = initTable[string, string]() lastReload: Time @@ -159,6 +160,7 @@ the execution of certain statements: afterCodeReload: lastReload = now() resetProgramState() + ``` On each code reload, Nim will first execute all `beforeCodeReload`:idx: handlers registered in the previous version of the program and then all @@ -167,7 +169,7 @@ that any handlers appearing in modules that weren't reloaded will also be executed. To prevent this behavior, one can guard the code with the `hasModuleChanged()`:idx: API: -.. code-block:: Nim + ```Nim import mydb var myCache = initTable[Key, Value]() @@ -175,6 +177,7 @@ executed. To prevent this behavior, one can guard the code with the afterCodeReload: if hasModuleChanged(mydb): resetCache(myCache) + ``` The hot code reloading is based on dynamic library hot swapping in the native targets and direct manipulation of the global namespace in the JavaScript @@ -203,8 +206,7 @@ runtime demands of the example code above. An example of compiling ``nimhcr.nim`` and ``nimrtl.nim`` when the source dir of Nim is installed with choosenim follows. -.. code:: console - + ```console # Unix/MacOS # Make sure you are in the directory containing your .nim files $ cd your-source-directory @@ -215,6 +217,7 @@ with choosenim follows. # verify that you have two files named libnimhcr and libnimrtl in your # source directory (.dll for Windows, .so for Unix, .dylib for MacOS) + ``` All modules of the project will be compiled to separate dynamic link libraries placed in the `nimcache` directory. Please note that during diff --git a/doc/idetools.rst b/doc/idetools.md similarity index 77% rename from doc/idetools.rst rename to doc/idetools.md index dcafaf45f4b3..8f4b3e995e39 100644 --- a/doc/idetools.rst +++ b/doc/idetools.md @@ -10,24 +10,21 @@ .. contents:: -.. raw:: html -

    - "yes, I'm the creator" -- Araq, 2013-07-26 19:28:32. -

    +> "yes, I'm the creator" -- Araq, 2013-07-26 19:28:32. -Note: this is mostly outdated, see instead `nimsuggest `_ +Note: this is mostly outdated, see instead [nimsuggest](nimsuggest.html) Nim differs from many other compilers in that it is really fast, and being so fast makes it suited to provide external queries for text editors about the source code being written. Through the -`idetools` command of `the compiler `_, any IDE +`idetools` command of [the compiler](nimc.html), any IDE can query a `.nim` source file and obtain useful information like definition of symbols or suggestions for completion. This document will guide you through the available options. If you want to look at practical examples of idetools support you can look -at the test files found in the `Test suite`_ or `various editor -integrations `_ +at the test files found in the [Test suite] or [various editor +integrations](https://github.com/Araq/Nim/wiki/Editor-Support) already available. @@ -48,30 +45,30 @@ Or:: nim idetools --trackDirty:DIRTY_FILE,FILE,LINE,COL proj.nim `proj.nim` - This is the main *project* filename. Most of the time you will +: This is the main *project* filename. Most of the time you will pass in the same as **FILE**, but for bigger projects this is the file which is used as main entry point for the program, the one which users compile to generate a final binary. `` - This would be any of the other idetools available options, like +: This would be any of the other idetools available options, like `--def` or `--suggest` explained in the following sections. `COL` - An integer with the column you are going to query. For the +: An integer with the column you are going to query. For the compiler columns start at zero, so the first column will be **0** and the last in an 80 column terminal will be **79**. `LINE` - An integer with the line you are going to query. For the compiler +: An integer with the line you are going to query. For the compiler lines start at **1**. `FILE` - The file you want to perform the query on. Usually you will +: The file you want to perform the query on. Usually you will pass in the same value as **proj.nim**. `DIRTY_FILE` - The **FILE** parameter 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 @@ -127,8 +124,8 @@ to the case insensitiveness of the language (plus underscores as separators!). The typical usage scenario for this option is to call it after the -user has typed the dot character for `the object oriented call -syntax `_. +user has typed the dot character for [the object oriented call +syntax](tut2.html#object-oriented-programming-method-call-syntax). Idetools will try to return the suggestions sorted first by scope (from innermost to outermost) and then by item name. @@ -199,7 +196,7 @@ of text it thinks necessary plus an empty line to indicate the end of the answer. You can find examples of client/server communication in the idetools -tests found in the `Test suite`_. +tests found in the [Test suite]. Parsing idetools output @@ -242,45 +239,48 @@ symbol for which idetools returns valid output. skConst ------- -| **Third column**: module + [n scope nesting] + const name. +| **Third column**: module + \[n scope nesting] + const name. | **Fourth column**: the type of the const value. | **Docstring**: always the empty string. -.. code-block:: nim - const SOME_SEQUENCE = @[1, 2] - --> col 2: $MODULE.SOME_SEQUENCE - col 3: seq[int] - col 7: "" + ```nim + const SOME_SEQUENCE = @[1, 2] + --> col 2: $MODULE.SOME_SEQUENCE + col 3: seq[int] + col 7: "" + ``` skEnumField ----------- -| **Third column**: module + [n scope nesting] + enum type + enum field name. +| **Third column**: module + \[n scope nesting] + enum type + enum field name. | **Fourth column**: enum type grouping other enum fields. | **Docstring**: always the empty string. -.. code-block:: nim - Open(filename, fmWrite) - --> col 2: system.FileMode.fmWrite - col 3: FileMode - col 7: "" + ```nim + Open(filename, fmWrite) + --> col 2: system.FileMode.fmWrite + col 3: FileMode + col 7: "" + ``` skForVar -------- -| **Third column**: module + [n scope nesting] + var name. +| **Third column**: module + \[n scope nesting] + var name. | **Fourth column**: type of the var. | **Docstring**: always the empty string. -.. code-block:: nim - proc looper(filename = "tests.nim") = - for letter in filename: - echo letter - --> col 2: $MODULE.looper.letter - col 3: char - col 7: "" + ```nim + proc looper(filename = "tests.nim") = + for letter in filename: + echo letter + --> col 2: $MODULE.looper.letter + col 3: char + col 7: "" + ``` skIterator, skClosureIterator @@ -291,48 +291,51 @@ defined, since at that point in the file the parser hasn't processed the full line yet. The signature will be returned complete in posterior instances of the iterator. -| **Third column**: module + [n scope nesting] + iterator name. +| **Third column**: module + \[n scope nesting] + iterator name. | **Fourth column**: signature of the iterator including return type. | **Docstring**: docstring if available. -.. code-block:: nim - let - text = "some text" - letters = toSeq(runes(text)) - --> col 2: unicode.runes - col 3: iterator (string): Rune - col 7: "iterates over any unicode character of the string `s`." + ```nim + let + text = "some text" + letters = toSeq(runes(text)) + --> col 2: unicode.runes + col 3: iterator (string): Rune + col 7: "iterates over any unicode character of the string `s`." + ``` skLabel ------- -| **Third column**: module + [n scope nesting] + name. +| **Third column**: module + \[n scope nesting] + name. | **Fourth column**: always the empty string. | **Docstring**: always the empty string. -.. code-block:: nim - proc test(text: string) = - var found = -1 - block loops: - --> col 2: $MODULE.test.loops - col 3: "" - col 7: "" + ```nim + proc test(text: string) = + var found = -1 + block loops: + --> col 2: $MODULE.test.loops + col 3: "" + col 7: "" + ``` skLet ----- -| **Third column**: module + [n scope nesting] + let name. +| **Third column**: module + \[n scope nesting] + let name. | **Fourth column**: the type of the let variable. | **Docstring**: always the empty string. -.. code-block:: nim - let - text = "some text" - --> col 2: $MODULE.text - col 3: string - col 7: "" + ```nim + let + text = "some text" + --> col 2: $MODULE.text + col 3: string + col 7: "" + ``` skMacro @@ -343,16 +346,17 @@ defined, since at that point in the file the parser hasn't processed the full line yet. The signature will be returned complete in posterior instances of the macro. -| **Third column**: module + [n scope nesting] + macro name. +| **Third column**: module + \[n scope nesting] + macro name. | **Fourth column**: signature of the macro including return type. | **Docstring**: docstring if available. -.. code-block:: nim - proc testMacro() = - expect(EArithmetic): - --> col 2: idetools_api.expect - col 3: proc (varargs[expr], stmt): stmt - col 7: "" + ```nim + proc testMacro() = + expect(EArithmetic): + --> col 2: idetools_api.expect + col 3: proc (varargs[expr], stmt): stmt + col 7: "" + ``` skMethod @@ -363,8 +367,8 @@ defined, since at that point in the file the parser hasn't processed the full line yet. The signature will be returned complete in posterior instances of the method. -Methods imply `dynamic dispatch -`_ and +Methods imply [dynamic dispatch]( +tut2.html#object-oriented-programming-dynamic-dispatch) and idetools performs a static analysis on the code. For this reason idetools may not return the definition of the correct method you are querying because it may be impossible to know until the code @@ -380,33 +384,35 @@ Note that at the moment the word `proc` is returned for the signature of the found method instead of the expected `method`. This may change in the future. -| **Third column**: module + [n scope nesting] + method name. +| **Third column**: module + \[n scope nesting] + method name. | **Fourth column**: signature of the method including return type. | **Docstring**: docstring if available. -.. code-block:: nim - method eval(e: PExpr): int = quit "to override!" - method eval(e: PLiteral): int = e.x - method eval(e: PPlusExpr): int = eval(e.a) + eval(e.b) - echo eval(newPlus(newPlus(newLit(1), newLit(2)), newLit(4))) - --> col 2: $MODULE.eval - col 3: proc (PPlusExpr): int - col 7: "" + ```nim + method eval(e: PExpr): int = quit "to override!" + method eval(e: PLiteral): int = e.x + method eval(e: PPlusExpr): int = eval(e.a) + eval(e.b) + echo eval(newPlus(newPlus(newLit(1), newLit(2)), newLit(4))) + --> col 2: $MODULE.eval + col 3: proc (PPlusExpr): int + col 7: "" + ``` skParam ------- -| **Third column**: module + [n scope nesting] + param name. +| **Third column**: module + \[n scope nesting] + param name. | **Fourth column**: the type of the parameter. | **Docstring**: always the empty string. -.. code-block:: nim - proc reader(filename = "tests.nim") = - let text = readFile(filename) - --> col 2: $MODULE.reader.filename - col 3: string - col 7: "" + ```nim + proc reader(filename = "tests.nim") = + let text = readFile(filename) + --> col 2: $MODULE.reader.filename + col 3: string + col 7: "" + ``` skProc @@ -421,34 +427,36 @@ While at the language level a proc is differentiated from others by the parameters and return value, the signature of the proc returned by idetools returns also the pragmas for the proc. -| **Third column**: module + [n scope nesting] + proc name. +| **Third column**: module + \[n scope nesting] + proc name. | **Fourth column**: signature of the proc including return type. | **Docstring**: docstring if available. -.. code-block:: nim - open(filename, fmWrite) - --> col 2: system.Open - col 3: proc (var File, string, FileMode, int): bool - col 7: - "Opens a file named `filename` with given `mode`. + ```nim + open(filename, fmWrite) + --> col 2: system.Open + col 3: proc (var File, string, FileMode, int): bool + col 7: + "Opens a file named `filename` with given `mode`. - Default mode is readonly. Returns true iff the file could be opened. - This throws no exception if the file could not be opened." + Default mode is readonly. Returns true iff the file could be opened. + This throws no exception if the file could not be opened." + ``` skResult -------- -| **Third column**: module + [n scope nesting] + result. +| **Third column**: module + \[n scope nesting] + result. | **Fourth column**: the type of the result. | **Docstring**: always the empty string. -.. code-block:: nim - proc getRandomValue() : int = - return 4 - --> col 2: $MODULE.getRandomValue.result - col 3: int - col 7: "" + ```nim + proc getRandomValue() : int = + return 4 + --> col 2: $MODULE.getRandomValue.result + col 3: int + col 7: "" + ``` skTemplate @@ -459,11 +467,11 @@ defined, since at that point in the file the parser hasn't processed the full line yet. The signature will be returned complete in posterior instances of the template. -| **Third column**: module + [n scope nesting] + template name. +| **Third column**: module + \[n scope nesting] + template name. | **Fourth column**: signature of the template including return type. | **Docstring**: docstring if available. -.. code-block:: nim + `````nim let text = "some text" letters = toSeq(runes(text)) @@ -474,45 +482,49 @@ posterior instances of the template. Example: - .. code-block:: nim + ```nim let numeric = @[1, 2, 3, 4, 5, 6, 7, 8, 9] odd_numbers = toSeq(filter(numeric) do (x: int) -> bool: if x mod 2 == 1: result = true) assert odd_numbers == @[1, 3, 5, 7, 9]" + ``` + ````` skType ------ -| **Third column**: module + [n scope nesting] + type name. +| **Third column**: module + \[n scope nesting] + type name. | **Fourth column**: the type. | **Docstring**: always the empty string. -.. code-block:: nim - proc writeTempFile() = - var output: File - --> col 2: system.File - col 3: File - col 7: "" + ```nim + proc writeTempFile() = + var output: File + --> col 2: system.File + col 3: File + col 7: "" + ``` skVar ----- -| **Third column**: module + [n scope nesting] + var name. +| **Third column**: module + \[n scope nesting] + var name. | **Fourth column**: the type of the var. | **Docstring**: always the empty string. -.. code-block:: nim - proc writeTempFile() = - var output: File - output.open("/tmp/somefile", fmWrite) - output.write("test") - --> col 2: $MODULE.writeTempFile.output - col 3: File - col 7: "" + ```nim + proc writeTempFile() = + var output: File + output.open("/tmp/somefile", fmWrite) + output.write("test") + --> col 2: $MODULE.writeTempFile.output + col 3: File + col 7: "" + ``` Test suite @@ -578,7 +590,7 @@ All the `tests/caas/*.txt` files encode a session with the compiler: modes, you can prefix a line with the mode and the line will be processed only in that mode. -* The rest of the line is treated as a `regular expression `_, +* The rest of the line is treated as a [regular expression](re.html), so be careful escaping metacharacters like parenthesis. Before the line is processed as a regular expression, some basic diff --git a/doc/intern.rst b/doc/intern.md similarity index 63% rename from doc/intern.rst rename to doc/intern.md index a8846ae02c87..fe2e59f51292 100644 --- a/doc/intern.rst +++ b/doc/intern.md @@ -10,7 +10,7 @@ .. include:: rstcommon.rst .. contents:: - "Abstraction is layering ignorance on top of reality." -- Richard Gabriel +> "Abstraction is layering ignorance on top of reality." -- Richard Gabriel Directory structure @@ -42,29 +42,29 @@ Bootstrapping the compiler Compiling the compiler is a simple matter of running: -.. code:: cmd - + ```cmd nim c koch.nim koch boot -d:release + ``` For a debug version use: -.. code:: cmd - + ```cmd nim c koch.nim koch boot + ``` And for a debug version compatible with GDB: -.. code:: cmd - + ```cmd nim c koch.nim koch boot --debuginfo --linedir:on + ``` The `koch`:cmd: program is Nim's maintenance script. It is a replacement for make and shell scripting with the advantage that it is much more portable. -More information about its options can be found in the `koch `_ +More information about its options can be found in the [koch](koch.html) documentation. @@ -73,61 +73,222 @@ Reproducible builds Set the compilation timestamp with the `SOURCE_DATE_EPOCH` environment variable. -.. code:: cmd - + ```cmd export SOURCE_DATE_EPOCH=$(git log -n 1 --format=%at) koch boot # or `./build_all.sh` + ``` -Developing the compiler -======================= +Debugging the compiler +====================== -To create a new compiler for each run, use `koch temp`:cmd:\: -.. code:: cmd - - koch temp c test.nim +Bisecting for regressions +------------------------- -`koch temp`:cmd: creates a debug build of the compiler, which is useful -to create stacktraces for compiler debugging. +There are often times when there is a bug that is caused by a regression in the +compiler or stdlib. Bisecting the Nim repo commits is a useful tool to identify +what commit introduced the regression. -You can of course use GDB or Visual Studio to debug the -compiler (via `--debuginfo --lineDir:on`:option:). However, there -are also lots of procs that aid in debugging: +Even if it's not known whether a bug is caused by a regression, bisection can reduce +debugging time by ruling it out. If the bug is found to be a regression, then you +focus on the changes introduced by that one specific commit. +`koch temp`:cmd: returns 125 as the exit code in case the compiler +compilation fails. This exit code tells `git bisect`:cmd: to skip the +current commit: -.. code-block:: nim + ```cmd + git bisect start bad-commit good-commit + git bisect run ./koch temp -r c test-source.nim + ``` - # dealing with PNode: +You can also bisect using custom options to build the compiler, for example if +you don't need a debug version of the compiler (which runs slower), you can replace +`./koch temp`:cmd: by explicit compilation command, see [Bootstrapping the compiler]. + + +Building an instrumented compiler +--------------------------------- + +Considering that a useful method of debugging the compiler is inserting debug +logging, or changing code and then observing the outcome of a testcase, it is +fastest to build a compiler that is instrumented for debugging from an +existing release build. `koch temp`:cmd: provides a convenient method of doing +just that. + +By default, running `koch temp`:cmd: will build a lean version of the compiler +with `-d:debug`:option: enabled. The compiler is written to `bin/nim_temp` by +default. A lean version of the compiler lacks JS and documentation generation. + +`bin/nim_temp` can be directly used to run testcases, or used with testament +with `testament --nim:bin/nim_temp r tests/category/tsometest`:cmd:. + +`koch temp`:cmd: will build the temporary compiler with the `-d:debug`:option: +enabled. Here are compiler options that are of interest when debugging: + +* `-d:debug`:option:\: enables `assert` statements and stacktraces and all + runtime checks +* `--opt:speed`:option:\: build with optimizations enabled +* `--debugger:native`:option:\: enables `--debuginfo --lineDir:on`:option: for using + a native debugger like GDB, LLDB or CDB +* `-d:nimDebug`:option: cause calls to `quit` to raise an assertion exception +* `-d:nimDebugUtils`:option:\: enables various debugging utilities; + see `compiler/debugutils` +* `-d:stacktraceMsgs -d:nimCompilerStacktraceHints`:option:\: adds some additional + stacktrace hints; see https://github.com/nim-lang/Nim/pull/13351 +* `-u:leanCompiler`:option:\: enable JS and doc generation + +Another method to build and run the compiler is directly through `koch`:cmd:\: + + ```cmd + koch temp [options] c test.nim + + # (will build with js support) + koch temp [options] js test.nim + + # (will build with doc support) + koch temp [options] doc test.nim + ``` + +Debug logging +------------- + +"Printf debugging" is still the most appropriate way to debug many problems +arising in compiler development. The typical usage of breakpoints to debug +the code is often less practical, because almost all code paths in the +compiler will be executed hundreds of times before a particular section of the +tested program is reached where the newly developed code must be activated. + +To work around this problem, you'll typically introduce an if statement in the +compiler code detecting more precisely the conditions where the tested feature +is being used. One very common way to achieve this is to use the `mdbg` condition, +which will be true only in contexts, processing expressions and statements from +the currently compiled main module: + + ```nim + # inside some compiler module + if mdbg: + debug someAstNode + ``` + +Using the `isCompilerDebug`:nim: condition along with inserting some statements +into the testcase provides more granular logging: + + ```nim + # compilermodule.nim + if isCompilerDebug(): + debug someAstNode + + # testcase.nim + proc main = + {.define(nimCompilerDebug).} + let a = 2.5 * 3 + {.undef(nimCompilerDebug).} + ``` + +Logging can also be scoped to a specific filename as well. This will of course +match against every module with that name. + + ```nim + if `??`(conf, n.info, "module.nim"): + debug(n) + ``` + +The above examples also makes use of the `debug`:nim: proc, which is able to +print a human-readable form of an arbitrary AST tree. Other common ways to print +information about the internal compiler types include: + + ```nim + # pretty print PNode + + # pretty prints the Nim ast echo renderTree(someNode) - debug(someNode) # some JSON representation - # dealing with PType: + # pretty prints the Nim ast, but annotates symbol IDs + echo renderTree(someNode, {renderIds}) + + # pretty print ast as JSON + debug(someNode) + + # print as YAML + echo treeToYaml(config, someNode) + + + # pretty print PType + + # print type name echo typeToString(someType) + + # pretty print as JSON debug(someType) - # dealing with PSym: + # print as YAML + echo typeToYaml(config, someType) + + + # pretty print PSym + + # print the symbol's name echo symbol.name.s + + # pretty print as JSON debug(symbol) - # pretty prints the Nim ast, but annotates symbol IDs: - echo renderTree(someNode, {renderIds}) - if `??`(conf, n.info, "temp.nim"): - # only output when it comes from "temp.nim" - echo renderTree(n) - if `??`(conf, n.info, "temp.nim"): - # why does it process temp.nim here? - writeStackTrace() + # print as YAML + echo symToYaml(config, symbol) + + + # pretty print TLineInfo + lineInfoToStr(lineInfo) + + + # print the structure of any type + repr(someVar) + ``` + +Here are some other helpful utilities: + + ```nim + # how did execution reach this location? + writeStackTrace() + ``` These procs may not already be imported by the module you're editing. You can import them directly for debugging: -.. code-block:: nim + ```nim from astalgo import debug from types import typeToString from renderer import renderTree from msgs import `??` + ``` +Native debugging +---------------- + +Stepping through the compiler with a native debugger is a very powerful tool to +both learn and debug it. However, there is still the need to constrain when +breakpoints are triggered. The same methods as in [Debug logging] can be applied +here when combined with calls to the debug helpers `enteringDebugSection()`:nim: +and `exitingDebugSection()`:nim:. + +#. Compile the temp compiler with `--debugger:native -d:nimDebugUtils`:option: +#. Set your desired breakpoints or watchpoints. +#. Configure your debugger: + * GDB: execute `source tools/compiler.gdb` at startup + * LLDB execute `command source tools/compiler.lldb` at startup +#. Use one of the scoping helpers like so: + + ```nim + if isCompilerDebug(): + enteringDebugSection() + else: + exitingDebugSection() + ``` + +A caveat of this method is that all breakpoints and watchpoints are enabled or +disabled. Also, due to a bug, only breakpoints can be constrained for LLDB. The compiler's architecture =========================== @@ -148,27 +309,10 @@ The syntax tree consists of nodes which may have an arbitrary number of children. Types and symbols are represented by other nodes, because they may contain cycles. The AST changes its shape after semantic checking. This is needed to make life easier for the code generators. See the "ast" module -for the type definitions. The `macros `_ module contains many +for the type definitions. The [macros](macros.html) module contains many examples how the AST represents each syntactic structure. -Bisecting for regressions -========================= - -`koch temp`:cmd: returns 125 as the exit code in case the compiler -compilation fails. This exit code tells `git bisect`:cmd: to skip the -current commit: - -.. code:: cmd - - git bisect start bad-commit good-commit - git bisect run ./koch temp -r c test-source.nim - -You can also bisect using custom options to build the compiler, for example if -you don't need a debug version of the compiler (which runs slower), you can replace -`./koch temp`:cmd: by explicit compilation command, see `Bootstrapping the compiler`_. - - Runtimes ======== @@ -180,14 +324,15 @@ ARC/ORC. The new runtime is active `when defined(nimV2)`. Coding Guidelines ================= -* We follow Nim's official style guide, see ``_. +* We follow Nim's official style guide, see [NEP1](nep1.html). * Max line length is 100 characters. * Provide spaces around binary operators if that enhances readability. * Use a space after a colon, but not before it. -* [deprecated] Start types with a capital `T`, unless they are +* (deprecated) Start types with a capital `T`, unless they are pointers/references which start with `P`. +* Prefer `import package`:nim: over `from package import symbol`:nim:. -See also the `API naming design `_ document. +See also the [API naming design](apis.html) document. Porting to new platforms @@ -229,7 +374,7 @@ Files that may need changed for your platform include: Add os/cpu compiler/linker flags. If the `--os` or `--cpu` options aren't passed to the compiler, then Nim will -determine the current host os, cpu and endianess from `system.cpuEndian`, +determine the current host os, cpu and endianness from `system.cpuEndian`, `system.hostOS` and `system.hostCPU`. Those values are derived from `compiler/platform.nim`. @@ -253,17 +398,17 @@ Runtime type information programming language: Garbage collection - The old GCs use the RTTI for traversing abitrary Nim types, but usually +: The old GCs use the RTTI for traversing arbitrary Nim types, but usually only the `marker` field which contains a proc that does the traversal. Complex assignments - Sequences and strings are implemented as - pointers to resizeable buffers, but Nim requires copying for +: Sequences and strings are implemented as + pointers to resizable buffers, but Nim requires copying for assignments. Apart from RTTI the compiler also generates copy procedures as a specialization. We already know the type information as a graph in the compiler. -Thus we need to serialize this graph as RTTI for C code generation. +Thus, we need to serialize this graph as RTTI for C code generation. Look at the file ``lib/system/hti.nim`` for more information. @@ -273,7 +418,7 @@ Magics and compilerProcs The `system` module contains the part of the RTL which needs support by compiler magic. The C code generator generates the C code for it, just like any other module. However, calls to some procedures like `addInt` are inserted by -the generator. Therefore there is a table (`compilerprocs`) +the generator. Therefore, there is a table (`compilerprocs`) with all symbols that are marked as `compilerproc`. `compilerprocs` are needed by the code generator. A `magic` proc is not the same as a `compilerproc`: A `magic` is a proc that needs compiler magic for its @@ -303,29 +448,31 @@ Tests with GCC on Amd64 showed that it's really beneficial if the Proper thunk generation is harder because the proc that is to wrap could stem from a complex expression: -.. code-block:: nim + ```nim receivesClosure(returnsDefaultCC[i]) + ``` -A thunk would need to call 'returnsDefaultCC[i]' somehow and that would require +A thunk would need to call `returnsDefaultCC[i]` somehow and that would require an *additional* closure generation... Ok, not really, but it requires to pass the function to call. So we'd end up with 2 indirect calls instead of one. -Another much more severe problem which this solution is that it's not GC-safe +Another much more severe problem with this solution is that it's not GC-safe to pass a proc pointer around via a generic `ref` type. Example code: -.. code-block:: nim + ```nim proc add(x: int): proc (y: int): int {.closure.} = return proc (y: int): int = return x + y var add2 = add(2) echo add2(5) #OUT 7 + ``` This should produce roughly this code: -.. code-block:: nim + ```nim type Env = ref object x: int # data @@ -342,11 +489,12 @@ This should produce roughly this code: var add2 = add(2) let tmp = if add2.data == nil: add2.prc(5) else: add2.prc(5, add2.data) echo tmp + ``` Beware of nesting: -.. code-block:: nim + ```nim proc add(x: int): proc (y: int): proc (z: int): int {.closure.} {.closure.} = return lambda (y: int): proc (z: int): int {.closure.} = return lambda (z: int): int = @@ -354,10 +502,11 @@ Beware of nesting: var add24 = add(2)(4) echo add24(5) #OUT 11 + ``` This should produce roughly this code: -.. code-block:: nim + ```nim type EnvX = ref object x: int # data @@ -379,12 +528,13 @@ This should produce roughly this code: proc add(x: int): tuple[prc, data: EnvX] = var ex: EnvX ex.x = x - result = (labmdaY, ex) + result = (lambdaY, ex) var tmp = add(2) var tmp2 = tmp.fn(4, tmp.data) var add24 = tmp2.fn(4, tmp2.data) echo add24(5) + ``` We could get rid of nesting environments by always inlining inner anon procs. @@ -395,8 +545,7 @@ however. Accumulator ----------- -.. code-block:: nim - + ```nim proc getAccumulator(start: int): proc (): int {.closure} = var i = start return lambda: int = @@ -415,18 +564,19 @@ Accumulator var a = accumulator(3) var b = accumulator(4) echo a() + b() + ``` Internals --------- Lambda lifting is implemented as part of the `transf` pass. The `transf` -pass generates code to setup the environment and to pass it around. However, +pass generates code to set up the environment and to pass it around. However, this pass does not change the types! So we have some kind of mismatch here; on the one hand the proc expression becomes an explicit tuple, on the other hand the tyProc(ccClosure) type is not changed. For C code generation it's also important the hidden formal param is `void*`:c: and not something more -specialized. However the more specialized env type needs to passed to the +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`! @@ -441,14 +591,14 @@ Integer literals ---------------- In Nim, there is a redundant way to specify the type of an -integer literal. First of all, it should be unsurprising that every +integer literal. First, it should be unsurprising that every node has a node kind. The node of an integer literal can be any of the following values:: nkIntLit, nkInt8Lit, nkInt16Lit, nkInt32Lit, nkInt64Lit, nkUIntLit, nkUInt8Lit, nkUInt16Lit, nkUInt32Lit, nkUInt64Lit -On top of that, there is also the `typ` field for the type. It the +On top of that, there is also the `typ` field for the type. The kind of the `typ` field can be one of the following ones, and it should be matching the literal kind:: @@ -469,36 +619,36 @@ keeps the full `int literal(321)` type. Here is an example where that difference matters. -.. code-block:: nim - - proc foo(arg: int8) = - echo "def" + ```nim + proc foo(arg: int8) = + echo "def" - const tmp1 = 123 - foo(tmp1) # OK + const tmp1 = 123 + foo(tmp1) # OK - let tmp2 = 123 - foo(tmp2) # Error + let tmp2 = 123 + foo(tmp2) # Error + ``` In a context with multiple overloads, the integer literal kind will always prefer the `int` type over all other types. If none of the overloads is of type `int`, then there will be an error because of ambiguity. -.. code-block:: nim - - proc foo(arg: int) = - echo "abc" - proc foo(arg: int8) = - echo "def" - foo(123) # output: abc + ```nim + proc foo(arg: int) = + echo "abc" + proc foo(arg: int8) = + echo "def" + foo(123) # output: abc - proc bar(arg: int16) = - echo "abc" - proc bar(arg: int8) = - echo "def" + proc bar(arg: int16) = + echo "abc" + proc bar(arg: int8) = + echo "def" - bar(123) # Error ambiguous call + bar(123) # Error ambiguous call + ``` In the compiler these integer literal types are represented with the node kind `nkIntLit`, type kind `tyInt` and the member `n` of the type diff --git a/doc/koch.rst b/doc/koch.md similarity index 86% rename from doc/koch.rst rename to doc/koch.md index b8d85dff495b..8fa19ce44874 100644 --- a/doc/koch.rst +++ b/doc/koch.md @@ -8,10 +8,7 @@ .. include:: rstcommon.rst .. contents:: -.. raw:: html -

    - "A great chef is an artist that I truly respect" -- Robert Stack. -

    +> "A great chef is an artist that I truly respect" -- Robert Stack. Introduction @@ -38,6 +35,9 @@ options: unless you are debugging the compiler. -d:nimUseLinenoise Use the linenoise library for interactive mode (not needed on Windows). +-d:leanCompiler Produce a compiler without JS codegen or + documentation generator in order to use less RAM + for bootstrapping. After compilation is finished you will hopefully end up with the nim compiler in the `bin` directory. You can add Nim's `bin` directory to @@ -48,16 +48,16 @@ csource command --------------- The `csource`:idx: command builds the C sources for installation. It accepts -the same options as you would pass to the `boot command -<#commands-boot-command>`_. +the same options as you would pass to the [boot command]( +#commands-boot-command). temp command ------------ The temp command builds the Nim compiler but with a different final name (`nim_temp`:cmd:), so it doesn't overwrite your normal compiler. You can use -this command to test different options, the same you would issue for the `boot -command <#commands-boot-command>`_. +this command to test different options, the same you would issue for the [boot +command](#commands-boot-command). test command ------------ @@ -88,4 +88,4 @@ pdf command The `pdf`:idx: command builds PDF versions of Nim documentation: Manual, Tutorial and a few other documents. To run it one needs to -`install Latex/xelatex `_ first. +[install Latex/xelatex](https://www.latex-project.org/get) first. diff --git a/doc/lib.rst b/doc/lib.md similarity index 75% rename from doc/lib.rst rename to doc/lib.md index f720127779a9..468ee84e325f 100644 --- a/doc/lib.rst +++ b/doc/lib.md @@ -15,14 +15,14 @@ Pure libraries do not depend on any external ``*.dll`` or ``lib*.so`` binary while impure libraries do. A wrapper is an impure library that is a very low-level interface to a C library. -Read this `document `_ for a quick overview of the API design. +Read [this document](apis.html) for a quick overview of the API design. Nimble ====== Nim's standard library only covers the basics, check -out ``_ for a list of 3rd party packages. +out https://nimble.directory/ for a list of 3rd party packages. Pure libraries @@ -31,17 +31,17 @@ Pure libraries Automatic imports ----------------- -* `system `_ +* [system](system.html) Basic procs and operators that every program needs. It also provides IO facilities for reading and writing text and binary files. It is imported implicitly by the compiler. Do not import it directly. It relies on compiler magic to work. -* `threads `_ +* [threads](threads.html) Basic Nim thread support. **Note:** This is part of the system module. Do not import it explicitly. Enabled with `--threads:on`:option:. -* `channels_builtin `_ +* [channels_builtin](channels_builtin.html) Nim message passing support for threads. **Note:** This is part of the system module. Do not import it explicitly. Enabled with `--threads:on`:option:. @@ -49,40 +49,40 @@ Automatic imports Core ---- -* `atomics `_ +* [atomics](atomics.html) Types and operations for atomic operations and lockless algorithms. -* `bitops `_ +* [bitops](bitops.html) Provides a series of low-level methods for bit manipulation. -* `cpuinfo `_ +* [cpuinfo](cpuinfo.html) This module implements procs to determine the number of CPUs / cores. -* `endians `_ +* [endians](endians.html) This module contains helpers that deal with different byte orders. -* `lenientops `_ +* [lenientops](lenientops.html) Provides binary operators for mixed integer/float expressions for convenience. -* `locks `_ +* [locks](locks.html) Locks and condition variables for Nim. -* `macrocache `_ +* [macrocache](macrocache.html) Provides an API for macros to collect compile-time information across modules. -* `macros `_ +* [macros](macros.html) Contains the AST API and documentation of Nim for writing macros. -* `rlocks `_ +* [rlocks](rlocks.html) Reentrant locks for Nim. -* `typeinfo `_ +* [typeinfo](typeinfo.html) Provides (unsafe) access to Nim's run-time type information. -* `typetraits `_ +* [typetraits](typetraits.html) This module defines compile-time reflection procs for working with types. -* `volatile `_ +* [volatile](volatile.html) This module contains code for generating volatile loads and stores, which are useful in embedded and systems programming. @@ -90,297 +90,301 @@ Core Algorithms ---------- -* `algorithm `_ +* [algorithm](algorithm.html) This module implements some common generic algorithms like sort or binary search. -* `enumutils `_ +* [enumutils](enumutils.html) This module adds functionality for the built-in `enum` type. -* `sequtils `_ +* [sequtils](sequtils.html) This module implements operations for the built-in `seq` type which were inspired by functional programming languages. -* `setutils `_ +* [setutils](setutils.html) This module adds functionality for the built-in `set` type. Collections ----------- -* `critbits `_ +* [critbits](critbits.html) This module implements a *crit bit tree* which is an efficient container for a sorted set of strings, or a sorted mapping of strings. -* `deques `_ +* [deques](deques.html) Implementation of a double-ended queue. The underlying implementation uses a `seq`. -* `heapqueue `_ +* [heapqueue](heapqueue.html) Implementation of a binary heap data structure that can be used as a priority queue. -* `intsets `_ +* [intsets](intsets.html) Efficient implementation of a set of ints as a sparse bit set. -* `lists `_ +* [lists](lists.html) Nim linked list support. Contains singly and doubly linked lists and circular lists ("rings"). -* `options `_ +* [options](options.html) The option type encapsulates an optional value. -* `packedsets `_ +* [packedsets](packedsets.html) Efficient implementation of a set of ordinals as a sparse bit set. -* `sets `_ +* [sets](sets.html) Nim hash set support. -* `tables `_ +* [tables](tables.html) Nim hash table support. Contains tables, ordered tables, and count tables. String handling --------------- -* `cstrutils `_ +* [cstrutils](cstrutils.html) Utilities for `cstring` handling. -* `editdistance `_ +* [editdistance](editdistance.html) This module contains an algorithm to compute the edit distance between two Unicode strings. -* `encodings `_ +* [encodings](encodings.html) Converts between different character encodings. On UNIX, this uses the `iconv` library, on Windows the Windows API. -* `parseutils `_ +* [parseutils](parseutils.html) This module contains helpers for parsing tokens, numbers, identifiers, etc. -* `pegs `_ +* [pegs](pegs.html) This module contains procedures and operators for handling PEGs. -* `punycode `_ +* [punycode](punycode.html) Implements a representation of Unicode with the limited ASCII character subset. -* `ropes `_ +* [ropes](ropes.html) This module contains support for a *rope* data type. Ropes can represent very long strings efficiently; in particular, concatenation is done in O(1) instead of O(n). -* `strbasics `_ +* [strbasics](strbasics.html) This module provides some high performance string operations. -* `strformat `_ +* [strformat](strformat.html) Macro based standard string interpolation/formatting. Inspired by Python's f-strings. -* `strmisc `_ +* [strmisc](strmisc.html) This module contains uncommon string handling operations that do not fit with the commonly used operations in strutils. -* `strscans `_ +* [strscans](strscans.html) This module contains a `scanf` macro for convenient parsing of mini languages. -* `strtabs `_ +* [strtabs](strtabs.html) The `strtabs` module implements an efficient hash table that is a mapping from strings to strings. Supports a case-sensitive, case-insensitive and style-insensitive modes. -* `strutils `_ +* [strutils](strutils.html) This module contains common string handling operations like changing case of a string, splitting a string into substrings, searching for substrings, replacing substrings. -* `unicode `_ +* [unicode](unicode.html) This module provides support to handle the Unicode UTF-8 encoding. -* `unidecode `_ +* [unidecode](unidecode.html) It provides a single proc that does Unicode to ASCII transliterations. Based on Python's Unidecode module. -* `wordwrap `_ +* [wordwrap](wordwrap.html) This module contains an algorithm to wordwrap a Unicode string. Time handling ------------- -* `monotimes `_ +* [monotimes](monotimes.html) The `monotimes` module implements monotonic timestamps. -* `times `_ +* [times](times.html) The `times` module contains support for working with time. Generic Operating System Services --------------------------------- -* `distros `_ +* [distros](distros.html) This module implements the basics for OS distribution ("distro") detection and the OS's native package manager. Its primary purpose is to produce output for Nimble packages, but it also contains the widely used **Distribution** enum that is useful for writing platform-specific code. - See `packaging `_ for hints on distributing Nim using OS packages. + See [packaging](packaging.html) for hints on distributing Nim using OS packages. -* `dynlib `_ +* [dynlib](dynlib.html) This module implements the ability to access symbols from shared libraries. -* `marshal `_ +* [marshal](marshal.html) Contains procs for serialization and deserialization of arbitrary Nim data structures. -* `memfiles `_ +* [memfiles](memfiles.html) This module provides support for memory-mapped files (Posix's `mmap`) on the different operating systems. -* `os `_ +* [os](os.html) Basic operating system facilities like retrieving environment variables, reading command line arguments, working with directories, running shell commands, etc. -* `osproc `_ +* [osproc](osproc.html) Module for process communication beyond `os.execShellCmd`. -* `streams `_ +* [streams](streams.html) This module provides a stream interface and two implementations thereof: the `FileStream` and the `StringStream` which implement the stream interface for Nim file objects (`File`) and strings. Other modules may provide other implementations for this standard stream interface. -* `terminal `_ +* [terminal](terminal.html) This module contains a few procedures to control the *terminal* (also called *console*). The implementation simply uses ANSI escape sequences and does not depend on any other module. + +* [tempfiles](tempfiles.html) + This module provides some utils to generate temporary path names and + create temporary files and directories. Math libraries -------------- -* `complex `_ +* [complex](complex.html) This module implements complex numbers and relevant mathematical operations. -* `fenv `_ +* [fenv](fenv.html) Floating-point environment. Handling of floating-point rounding and exceptions (overflow, zero-divide, etc.). -* `math `_ +* [math](math.html) Mathematical operations like cosine, square root. -* `random `_ +* [random](random.html) Fast and tiny random number generator. -* `rationals `_ +* [rationals](rationals.html) This module implements rational numbers and relevant mathematical operations. -* `stats `_ +* [stats](stats.html) Statistical analysis. -* `sums `_ +* [sums](sums.html) Accurate summation functions. -* `sysrand `_ +* [sysrand](sysrand.html) Cryptographically secure pseudorandom number generator. Internet Protocols and Support ------------------------------ -* `asyncdispatch `_ +* [asyncdispatch](asyncdispatch.html) This module implements an asynchronous dispatcher for IO operations. -* `asyncfile `_ +* [asyncfile](asyncfile.html) This module implements asynchronous file reading and writing using `asyncdispatch`. -* `asyncftpclient `_ - This module implements an asynchronous FTP client using the `asyncnet` +* `asyncftpclient](asyncftpclient.html) + [his module implements an asynchronous FTP client using the `asyncnet` module. -* `asynchttpserver `_ - This module implements an asynchronous HTTP server using the `asyncnet` +* `asynchttpserver](asynchttpserver.html) + [his module implements an asynchronous HTTP server using the `asyncnet` module. -* `asyncnet `_ +* [asyncnet](asyncnet.html) This module implements asynchronous sockets based on the `asyncdispatch` module. -* `asyncstreams `_ +* [asyncstreams](asyncstreams.html) This module provides `FutureStream` - a future that acts as a queue. -* `cgi `_ +* [cgi](cgi.html) This module implements helpers for CGI applications. -* `cookies `_ +* [cookies](cookies.html) This module contains helper procs for parsing and generating cookies. -* `httpclient `_ +* [httpclient](httpclient.html) This module implements a simple HTTP client which supports both synchronous and asynchronous retrieval of web pages. -* `mimetypes `_ +* [mimetypes](mimetypes.html) This module implements a mimetypes database. -* `nativesockets `_ +* [nativesockets](nativesockets.html) This module implements a low-level sockets API. -* `net `_ +* [net](net.html) This module implements a high-level sockets API. It replaces the `sockets` module. -* `selectors `_ +* [selectors](selectors.html) This module implements a selector API with backends specific to each OS. Currently, epoll on Linux and select on other operating systems. -* `smtp `_ +* [smtp](smtp.html) This module implements a simple SMTP client. -* `uri `_ +* [uri](uri.html) This module provides functions for working with URIs. Threading --------- -* `threadpool `_ - Implements Nim's `spawn `_. +* [threadpool](threadpool.html) + Implements Nim's [spawn](manual_experimental.html#parallel-amp-spawn). Parsers ------- -* `htmlparser `_ +* [htmlparser](htmlparser.html) This module parses an HTML document and creates its XML tree representation. -* `json `_ +* [json](json.html) High-performance JSON parser. -* `jsonutils `_ +* [jsonutils](jsonutils.html) This module implements a hookable (de)serialization for arbitrary types. -* `lexbase `_ +* [lexbase](lexbase.html) This is a low-level module that implements an extremely efficient buffering scheme for lexers and parsers. This is used by the diverse parsing modules. -* `parsecfg `_ +* [parsecfg](parsecfg.html) The `parsecfg` module implements a high-performance configuration file parser. The configuration file's syntax is similar to the Windows ``.ini`` format, but much more powerful, as it is not a line based parser. String literals, raw string literals, and triple quote string literals are supported as in the Nim programming language. -* `parsecsv `_ +* [parsecsv](parsecsv.html) The `parsecsv` module implements a simple high-performance CSV parser. -* `parsejson `_ - This module implements a JSON parser. It is used and exported by the `json `_ module, but can also be used in its own right. +* [parsejson](parsejson.html) + This module implements a JSON parser. It is used and exported by the [json](json.html) module, but can also be used in its own right. -* `parseopt `_ +* [parseopt](parseopt.html) The `parseopt` module implements a command line option parser. -* `parsesql `_ +* [parsesql](parsesql.html) The `parsesql` module implements a simple high-performance SQL parser. -* `parsexml `_ +* [parsexml](parsexml.html) The `parsexml` module implements a simple high performance XML/HTML parser. The only encoding that is supported is UTF-8. The parser has been designed to be somewhat error-correcting, so that even some "wild HTML" found on the @@ -390,37 +394,37 @@ Parsers Docutils -------- -* `packages/docutils/highlite `_ +* [packages/docutils/highlite](highlite.html) Source highlighter for programming or markup languages. Currently, only a few languages are supported, other languages may be added. The interface supports one language nested in another. -* `packages/docutils/rst `_ +* [packages/docutils/rst](rst.html) This module implements a reStructuredText parser. A large subset is implemented. Some features of the markdown wiki syntax are also supported. -* `packages/docutils/rstast `_ +* [packages/docutils/rstast](rstast.html) This module implements an AST for the reStructuredText parser. -* `packages/docutils/rstgen `_ +* [packages/docutils/rstgen](rstgen.html) This module implements a generator of HTML/Latex from reStructuredText. XML Processing -------------- -* `xmltree `_ +* [xmltree](xmltree.html) A simple XML tree. More efficient and simpler than the DOM. It also contains a macro for XML/HTML code generation. -* `xmlparser `_ +* [xmlparser](xmlparser.html) This module parses an XML document and creates its XML tree representation. Generators ---------- -* `htmlgen `_ +* [htmlgen](htmlgen.html) This module implements a simple XML and HTML code generator. Each commonly used HTML tag has a corresponding macro that generates a string with its HTML representation. @@ -429,81 +433,81 @@ Generators Hashing ------- -* `base64 `_ +* [base64](base64.html) This module implements a Base64 encoder and decoder. -* `hashes `_ +* [hashes](hashes.html) This module implements efficient computations of hash values for diverse Nim types. -* `md5 `_ +* [md5](md5.html) This module implements the MD5 checksum algorithm. -* `oids `_ +* [oids](oids.html) An OID is a global ID that consists of a timestamp, a unique counter, and a random value. This combination should suffice to produce a globally distributed unique ID. This implementation was extracted from the MongoDB interface and it thus binary compatible with a MongoDB OID. -* `sha1 `_ +* [sha1](sha1.html) This module implements the SHA-1 checksum algorithm. Miscellaneous ------------- -* `browsers `_ +* [browsers](browsers.html) This module implements procs for opening URLs with the user's default browser. -* `colors `_ +* [colors](colors.html) This module implements color handling for Nim. -* `coro `_ +* [coro](coro.html) This module implements experimental coroutines in Nim. -* `enumerate `_ +* [enumerate](enumerate.html) This module implements `enumerate` syntactic sugar based on Nim's macro system. -* `logging `_ +* [logging](logging.html) This module implements a simple logger. -* `segfaults `_ +* [segfaults](segfaults.html) Turns access violations or segfaults into a `NilAccessDefect` exception. -* `sugar `_ +* [sugar](sugar.html) This module implements nice syntactic sugar based on Nim's macro system. -* `unittest `_ +* [unittest](unittest.html) Implements a Unit testing DSL. -* `varints `_ +* [varints](varints.html) Decode variable-length integers that are compatible with SQLite. -* `with `_ +* [with](with.html) This module implements the `with` macro for easy function chaining. Modules for the JS backend -------------------------- -* `asyncjs `_ +* [asyncjs](asyncjs.html) Types and macros for writing asynchronous procedures in JavaScript. -* `dom `_ +* [dom](dom.html) Declaration of the Document Object Model for the JS backend. -* `jsbigints `_ +* [jsbigints](jsbigints.html) Arbitrary precision integers. -* `jsconsole `_ +* [jsconsole](jsconsole.html) Wrapper for the `console` object. -* `jscore `_ +* [jscore](jscore.html) The wrapper of core JavaScript functions. For most purposes, you should be using the `math`, `json`, and `times` stdlib modules instead of this module. -* `jsffi `_ +* [jsffi](jsffi.html) Types and macros for easier interaction with JavaScript. @@ -513,7 +517,7 @@ Impure libraries Regular expressions ------------------- -* `re `_ +* [re](re.html) This module contains procedures and operators for handling regular expressions. The current implementation uses PCRE. @@ -521,15 +525,15 @@ Regular expressions Database support ---------------- -* `db_postgres `_ +* [db_postgres](db_postgres.html) A higher level PostgreSQL database wrapper. The same interface is implemented for other databases too. -* `db_mysql `_ +* [db_mysql](db_mysql.html) A higher level MySQL database wrapper. The same interface is implemented for other databases too. -* `db_sqlite `_ +* [db_sqlite](db_sqlite.html) A higher level SQLite database wrapper. The same interface is implemented for other databases too. @@ -537,7 +541,7 @@ Database support Generic Operating System Services --------------------------------- -* `rdstdin `_ +* [rdstdin](rdstdin.html) This module contains code for reading from stdin. @@ -551,43 +555,43 @@ not contained in the distribution. You can then find them on the website. Windows-specific ---------------- -* `winlean `_ +* [winlean](winlean.html) Contains a wrapper for a small subset of the Win32 API. -* `registry `_ +* [registry](registry.html) Windows registry support. UNIX specific ------------- -* `posix `_ +* [posix](posix.html) Contains a wrapper for the POSIX standard. -* `posix_utils `_ +* [posix_utils](posix_utils.html) Contains helpers for the POSIX standard or specialized for Linux and BSDs. Regular expressions ------------------- -* `pcre `_ +* [pcre](pcre.html) Wrapper for the PCRE library. Database support ---------------- -* `postgres `_ +* [postgres](postgres.html) Contains a wrapper for the PostgreSQL API. -* `mysql `_ +* [mysql](mysql.html) Contains a wrapper for the mySQL API. -* `sqlite3 `_ +* [sqlite3](sqlite3.html) Contains a wrapper for the SQLite 3 API. -* `odbcsql `_ +* [odbcsql](odbcsql.html) interface to the ODBC driver. Network Programming and Internet Protocols ------------------------------------------ -* `openssl `_ +* [openssl](openssl.html) Wrapper for OpenSSL. diff --git a/doc/manual.rst b/doc/manual.md similarity index 90% rename from doc/manual.rst rename to doc/manual.md index adc1d9d16ba3..305e7861fe18 100644 --- a/doc/manual.rst +++ b/doc/manual.md @@ -10,9 +10,9 @@ Nim Manual .. contents:: - "Complexity" seems to be a lot like "energy": you can transfer it from the - end-user to one/some of the other players, but the total amount seems to remain - pretty much constant for a given task. -- Ran +> "Complexity" seems to be a lot like "energy": you can transfer it from the +> end-user to one/some of the other players, but the total amount seems to remain +> pretty much constant for a given task. -- Ran About this document @@ -22,16 +22,16 @@ About this document precise wording. This manual is constantly evolving into a proper specification. **Note**: The experimental features of Nim are -covered `here `_. +covered [here](manual_experimental.html). **Note**: Assignments, moves, and destruction are specified in -the `destructors `_ document. +the [destructors](destructors.html) document. This document describes the lexis, the syntax, and the semantics of the Nim language. To learn how to compile Nim programs and generate documentation see -the `Compiler User Guide `_ and the `DocGen Tools Guide `_. +the [Compiler User Guide](nimc.html) and the [DocGen Tools Guide](docgen.html). The language constructs are explained using an extended BNF, in which `(a)*` means 0 or more `a`'s, `a+` means 1 or more `a`'s, and `(a)?` means an @@ -91,11 +91,11 @@ The nature of this executable depends on the compiler implementation; it may, for example, be a native binary or JavaScript source code. In a typical Nim program, most of the code is compiled into the executable. -However, some of the code may be executed at +However, some code may be executed at `compile-time`:idx:. This can include constant expressions, macro definitions, and Nim procedures used by macro definitions. Most of the Nim language is -supported at compile-time, but there are some restrictions -- see `Restrictions -on Compile-Time Execution <#restrictions-on-compileminustime-execution>`_ for +supported at compile-time, but there are some restrictions -- see [Restrictions +on Compile-Time Execution] for details. We use the term `runtime`:idx: to cover both compile-time execution and code execution in the executable. @@ -111,22 +111,23 @@ A `panic`:idx: is an error that the implementation detects and reports at runtime. The method for reporting such errors is via *raising exceptions* or *dying with a fatal error*. However, the implementation provides a means to disable these `runtime checks`:idx:. See the section -pragmas_ for details. +[Pragmas] for details. Whether a panic results in an exception or in a fatal error is -implementation specific. Thus the following program is invalid; even though the +implementation specific. Thus, the following program is invalid; even though the code purports to catch the `IndexDefect` from an out-of-bounds array access, the compiler may instead choose to allow the program to die with a fatal error. -.. code-block:: nim + ```nim var a: array[0..1, char] let i = 5 try: a[i] = 'N' except IndexDefect: echo "invalid index" + ``` -The current implementation allows to switch between these different behaviors +The current implementation allows switching between these different behaviors via `--panics:on|off`:option:. When panics are turned on, the program dies with a panic, if they are turned off the runtime errors are turned into exceptions. The benefit of `--panics:on`:option: is that it produces smaller binary @@ -214,10 +215,11 @@ no other tokens between it and the preceding one, it does not start a new comment: -.. code-block:: nim + ```nim i = 0 # This is a single comment over multiple lines. # The lexer merges these two pieces. # The comment continues here. + ``` `Documentation comments`:idx: are comments that start with two `##`. @@ -231,26 +233,29 @@ Multiline comments Starting with version 0.13.0 of the language Nim supports multiline comments. They look like: -.. code-block:: nim + ```nim #[Comment here. Multiple lines are not a problem.]# + ``` Multiline comments support nesting: -.. code-block:: nim + ```nim #[ #[ Multiline comment in already commented out code. ]# proc p[T](x: T) = discard ]# + ``` Multiline documentation comments also exist and support nesting too: -.. code-block:: nim + ```nim proc foo = ##[Long documentation comment here. ]## + ``` Identifiers & Keywords @@ -263,10 +268,11 @@ and underscores, with the following restrictions: * does not end with an underscore `_` * two immediate following underscores `__` are not allowed: -.. code-block:: + ``` letter ::= 'A'..'Z' | 'a'..'z' | '\x80'..'\xff' digit ::= '0'..'9' IDENTIFIER ::= letter ( ['_'] (letter | digit) )* + ``` Currently, any Unicode character with an ordinal value > 127 (non-ASCII) is classified as a `letter` and may thus be part of an identifier but later @@ -275,8 +281,8 @@ operator characters instead. The following keywords are reserved and cannot be used as identifiers: -.. code-block:: nim - :file: keywords.txt + ```nim file="keywords.txt" + ``` Some keywords are unused; they are reserved for future developments of the language. @@ -287,10 +293,11 @@ Identifier equality Two identifiers are considered equal if the following algorithm returns true: -.. code-block:: nim + ```nim proc sameIdentifier(a, b: string): bool = a[0] == b[0] and a.replace("_", "").toLowerAscii == b.replace("_", "").toLowerAscii + ``` That means only the first letters are compared in a case-sensitive manner. Other letters are compared case-insensitively within the ASCII range and underscores are ignored. @@ -323,10 +330,11 @@ If a keyword is enclosed in backticks it loses its keyword property and becomes Examples -.. code-block:: nim + ```nim var `var` = "Hello Stropping" + ``` -.. code-block:: nim + ```nim type Obj = object `type`: int @@ -340,6 +348,7 @@ Examples const `assert` = true assert `assert` + ``` String literals @@ -363,7 +372,7 @@ contain the following `escape sequences`:idx:\ : ``\\`` `backslash`:idx: ``\"`` `quotation mark`:idx: ``\'`` `apostrophe`:idx: - ``\`` '0'..'9'+ `character with decimal value d`:idx:; + ``\`` '0'..'9'+ `character with decimal value d`:idx:; all decimal digits directly following are used for the character ``\a`` `alert`:idx: @@ -379,7 +388,7 @@ contain the following `escape sequences`:idx:\ : ================== =================================================== -Strings in Nim may contain any 8-bit value, even embedded zeros. However +Strings in Nim may contain any 8-bit value, even embedded zeros. However, some operations may interpret the first binary zero as a terminator. @@ -396,8 +405,9 @@ be whitespace between the opening `"""` and the newline), the newline (and the preceding whitespace) is not included in the string. The ending of the string literal is defined by the pattern `"""[^"]`, so this: -.. code-block:: nim + ```nim """"long string within quotes"""" + ``` Produces:: @@ -414,15 +424,15 @@ letter `r` (or `R`) and are delimited by matching double quotes (just like ordinary string literals) and do not interpret the escape sequences. This is especially convenient for regular expressions or Windows paths: -.. code-block:: nim - + ```nim var f = openFile(r"C:\texts\text.txt") # a raw string, so ``\t`` is no tab + ``` To produce a single `"` within a raw string literal, it has to be doubled: -.. code-block:: nim - + ```nim r"a""b" + ``` Produces:: @@ -489,15 +499,15 @@ Rationale: It enables the efficient support of `array[char, int]` or `set[char]`. The `Rune` type can represent any Unicode character. -`Rune` is declared in the `unicode module `_. +`Rune` is declared in the [unicode module](unicode.html). A character literal that does not end in `'` is interpreted as `'` if there -is a preceeding backtick token. There must be no whitespace between the preceeding +is a preceding backtick token. There must be no whitespace between the preceding backtick token and the character literal. This special case ensures that a declaration like ``proc `'customLiteral`(s: string)`` is valid. ``proc `'customLiteral`(s: string)`` is the same as ``proc `'\''customLiteral`(s: string)``. -See also `custom numeric literals <#custom-numeric-literals>`_. +See also [custom numeric literals]. Numeric literals @@ -556,31 +566,31 @@ an expression `-128'i8` should be valid and without this special case, this woul be impossible -- `128` is not a valid `int8` value, only `-128` is. For the `unary_minus` rule there are further restrictions that are not covered -in the formal grammar. For `-` to be part of the number literal its immediately -preceeding character has to be in the +in the formal grammar. For `-` to be part of the number literal the immediately +preceding character has to be in the set `{' ', '\t', '\n', '\r', ',', ';', '(', '[', '{'}`. This set was designed to cover most cases in a natural manner. In the following examples, `-1` is a single token: -.. code-block:: nim - + ```nim echo -1 echo(-1) echo [-1] echo 3,-1 "abc";-1 + ``` In the following examples, `-1` is parsed as two separate tokens (as `-`:tok: `1`:tok:): -.. code-block:: nim - + ```nim echo x-1 echo (int)-1 echo [a]-1 "abc"-1 + ``` The suffix starting with an apostrophe ('\'') is called a @@ -625,16 +635,14 @@ Hence: 0b10000000'u8 == 0x80'u8 == 128, but, 0b10000000'i8 == 0x80'i8 == -1 instead of causing an overflow error. -Custom numeric literals -~~~~~~~~~~~~~~~~~~~~~~~ +### Custom numeric literals If the suffix is not predefined, then the suffix is assumed to be a call to a proc, template, macro or other callable identifier that is passed the string containing the literal. The callable identifier needs to be declared with a special ``'`` prefix: -.. code-block:: nim - + ```nim import strutils type u4 = distinct uint8 # a 4-bit unsigned integer aka "nibble" proc `'u4`(n: string): u4 = @@ -642,20 +650,21 @@ with a special ``'`` prefix: result = (parseInt(n) and 0x0F).u4 var x = 5'u4 + ``` More formally, a custom numeric literal `123'custom` is transformed to r"123".`'custom` in the parsing step. There is no AST node kind that corresponds to this transformation. The transformation naturally handles the case that additional parameters are passed to the callee: -.. code-block:: nim - + ```nim import strutils type u4 = distinct uint8 # a 4-bit unsigned integer aka "nibble" proc `'u4`(n: string; moreData: int): u4 = result = (parseInt(n) and 0x0F).u4 var x = 5'u4(123) + ``` Custom numeric literals are covered by the grammar rule named `CUSTOM_NUMERIC_LIT`. A custom numeric literal is a single token. @@ -686,6 +695,21 @@ are used for other notational purposes. The `not` keyword is always a unary operator, `a not b` is parsed as `a(not b)`, not as `(a) not (b)`. +Unicode Operators +----------------- + +These Unicode operators are also parsed as operators:: + + ∙ ∘ × ★ ⊗ ⊘ ⊙ ⊛ ⊠ ⊡ ∩ ∧ ⊓ # same priority as * (multiplication) + ± ⊕ ⊖ ⊞ ⊟ ∪ ∨ ⊔ # same priority as + (addition) + + +Unicode operators can be combined with non-Unicode operator +symbols. The usual precedence extensions then apply, for example, `⊠=` is an +assignment like operator just like `*=` is. + +No Unicode normalization step is performed. + Other tokens ------------ @@ -704,7 +728,7 @@ Syntax ====== This section lists Nim's standard syntax. How the parser handles -the indentation is already described in the `Lexical Analysis`_ section. +the indentation is already described in the [Lexical Analysis] section. Nim allows user-definable operators. Binary operators have 11 different levels of precedence. @@ -717,12 +741,13 @@ Associativity Binary operators whose first character is `^` are right-associative, all other binary operators are left-associative. -.. code-block:: nim + ```nim proc `^/`(x, y: float): float = # a right-associative division operator result = x / y echo 12 ^/ 4 ^/ 8 # 24.0 (4 / 8 = 0.5, then 12 / 0.5 = 24.0) echo 12 / 4 / 8 # 0.375 (12 / 4 = 3.0, then 3 / 8 = 0.375) + ``` Precedence ---------- @@ -768,20 +793,23 @@ Precedence level Operators First Whether an operator is used as a prefix operator is also affected by preceding whitespace (this parsing change was introduced with version 0.13.0): -.. code-block:: nim + ```nim echo $foo # is parsed as echo($foo) + ``` Spacing also determines whether `(a, b)` is parsed as an argument list of a call or whether it is parsed as a tuple constructor: -.. code-block:: nim + ```nim echo(1, 2) # pass 1 and 2 to echo + ``` -.. code-block:: nim + ```nim echo (1, 2) # pass the tuple (1, 2) to echo + ``` Dot-like operators ------------------ @@ -808,9 +836,7 @@ Order of evaluation Order of evaluation is strictly left-to-right, inside-out as it is typical for most others imperative programming languages: -.. code-block:: nim - :test: "nim c $1" - + ```nim test = "nim c $1" var s = "" proc p(arg: int): int = @@ -820,14 +846,13 @@ imperative programming languages: discard p(p(1) + p(2)) doAssert s == "123" + ``` Assignments are not special, the left-hand-side expression is evaluated before the right-hand side: -.. code-block:: nim - :test: "nim c $1" - + ```nim test = "nim c $1" var v = 0 proc getI(): int = result = v @@ -845,6 +870,7 @@ right-hand side: someCopy(b[getI()], getI()) doAssert b == [1, 0, 0] + ``` Rationale: Consistency with overloaded assignment or assignment-like operations, @@ -855,9 +881,7 @@ However, the concept of "order of evaluation" is only applicable after the code was normalized: The normalization involves template expansions and argument reorderings that have been passed to named parameters: -.. code-block:: nim - :test: "nim c $1" - + ```nim test = "nim c $1" var s = "" proc p(): int = @@ -884,6 +908,7 @@ reorderings that have been passed to named parameters: construct(second = q(), first = p()) doAssert s == "qppqpq" + ``` Rationale: This is far easier to implement than hypothetical alternatives. @@ -920,8 +945,7 @@ of the Fibonacci series **at compile-time**. (This is a demonstration of flexibility in defining constants, not a recommended style for solving this problem.) -.. code-block:: nim - :test: "nim c $1" + ```nim test = "nim c $1" import std/strformat var fibN {.compileTime.}: int @@ -949,6 +973,7 @@ problem.) static: echo displayFib + ``` Restrictions on Compile-Time Execution @@ -1013,24 +1038,24 @@ Pre-defined integer types These integer types are pre-defined: `int` - the generic signed integer type; its size is platform-dependent and has the +: the generic signed integer type; its size is platform-dependent and has the same size as a pointer. This type should be used in general. An integer literal that has no type suffix is of this type if it is in the range `low(int32)..high(int32)` otherwise the literal's type is `int64`. `int`\ XX - additional signed integer types of XX bits use this naming scheme +: additional signed integer types of XX bits use this naming scheme (example: int16 is a 16-bit wide integer). The current implementation supports `int8`, `int16`, `int32`, `int64`. Literals of these types have the suffix 'iXX. `uint` - the generic `unsigned integer`:idx: type; its size is platform-dependent and +: the generic `unsigned integer`:idx: type; its size is platform-dependent and has the same size as a pointer. An integer literal with the type suffix `'u` is of this type. `uint`\ XX - additional unsigned integer types of XX bits use this naming scheme +: additional unsigned integer types of XX bits use this naming scheme (example: uint16 is a 16-bit wide unsigned integer). The current implementation supports `uint8`, `uint16`, `uint32`, `uint64`. Literals of these types have the suffix 'uXX. @@ -1056,17 +1081,6 @@ operation meaning `a %% b` unsigned integer modulo operation `a <% b` treat `a` and `b` as unsigned and compare `a <=% b` treat `a` and `b` as unsigned and compare -`ze(a)` extends the bits of `a` with zeros until it has the - width of the `int` type -`toU8(a)` treats `a` as unsigned and converts it to an - unsigned integer of 8 bits (but still the - `int8` type) -`toU16(a)` treats `a` as unsigned and converts it to an - unsigned integer of 16 bits (but still the - `int16` type) -`toU32(a)` treats `a` as unsigned and converts it to an - unsigned integer of 32 bits (but still the - `int32` type) ====================== ====================================================== `Automatic type conversion`:idx: is performed in expressions where different @@ -1077,20 +1091,20 @@ example `int32 -> int16`). A `widening type conversion`:idx: converts a smaller type to a larger type (for example `int16 -> int32`). In Nim only widening type conversions are *implicit*: -.. code-block:: nim + ```nim var myInt16 = 5i16 var myInt: int myInt16 + 34 # of type `int16` myInt16 + myInt # of type `int` myInt16 + 2i32 # of type `int32` + ``` However, `int` literals are implicitly convertible to a smaller integer type if the literal's value fits this smaller type and such a conversion is less expensive than other implicit conversions, so `myInt16 + 34` produces an `int16` result. -For further details, see `Convertible relation -<#type-relations-convertible-relation>`_. +For further details, see [Convertible relation]. Subrange types @@ -1099,11 +1113,12 @@ A subrange type is a range of values from an ordinal or floating-point type (the type). To define a subrange type, one must specify its limiting values -- the lowest and highest value of the type. For example: -.. code-block:: nim + ```nim type Subrange = range[0..5] PositiveFloat = range[0.0..Inf] Positive* = range[1..high(int)] # as defined in `system` + ``` `Subrange` is a subrange of an integer which can only hold the values 0 @@ -1124,22 +1139,21 @@ Pre-defined floating-point types The following floating-point types are pre-defined: `float` - the generic floating-point type; its size used to be platform-dependent, +: the generic floating-point type; its size used to be platform-dependent, but now it is always mapped to `float64`. This type should be used in general. `float`\ XX - an implementation may define additional floating-point types of XX bits using +: an implementation may define additional floating-point types of XX bits using this naming scheme (example: `float64` is a 64-bit wide float). The current implementation supports `float32` and `float64`. Literals of these types have the suffix 'fXX. -Automatic type conversion in expressions with different kinds -of floating-point types is performed: See `Convertible relation`_ for further -details. Arithmetic performed on floating-point types follows the IEEE -standard. Integer types are not converted to floating-point types automatically -and vice versa. +Automatic type conversion in expressions with different kinds of floating-point +types is performed: See [Convertible relation] for further details. Arithmetic +performed on floating-point types follows the IEEE standard. Integer types are +not converted to floating-point types automatically and vice versa. The IEEE standard defines five types of floating-point exceptions: @@ -1163,12 +1177,13 @@ These exceptions inherit from the `FloatingPointDefect`:idx: base class. Nim provides the pragmas `nanChecks`:idx: and `infChecks`:idx: to control whether the IEEE exceptions are ignored or trap a Nim exception: -.. code-block:: nim + ```nim {.nanChecks: on, infChecks: on.} var a = 1.0 var b = 0.0 echo b / b # raises FloatInvalidOpDefect echo a / b # raises FloatOverflowDefect + ``` In the current implementation `FloatDivByZeroDefect` and `FloatInexactDefect` are never raised. `FloatOverflowDefect` is raised instead of @@ -1200,11 +1215,11 @@ The operators `not, and, or, xor, <, <=, >, >=, !=, ==` are defined for the bool type. The `and` and `or` operators perform short-cut evaluation. Example: -.. code-block:: nim - + ```nim while p != nil and p.name != "xyz": # p.name is not evaluated if p == nil p = p.next + ``` The size of the bool type is one byte. @@ -1213,10 +1228,10 @@ The size of the bool type is one byte. Character type -------------- The character type is named `char` in Nim. Its size is one byte. -Thus it cannot represent a UTF-8 character, but a part of it. +Thus, it cannot represent a UTF-8 character, but a part of it. The `Rune` type is used for Unicode characters, it can represent any Unicode -character. `Rune` is declared in the `unicode module `_. +character. `Rune` is declared in the [unicode module](unicode.html). @@ -1226,11 +1241,11 @@ Enumeration types Enumeration types define a new type whose values consist of the ones specified. The values are ordered. Example: -.. code-block:: nim - + ```nim type Direction = enum north, east, south, west + ``` Now the following holds:: @@ -1244,7 +1259,7 @@ Now the following holds:: ord(Direction.west) == 3 The implied order is: north < east < south < west. The comparison operators can be used -with enumeration types. Instead of `north` etc, the enum value can also +with enumeration types. Instead of `north` etc., the enum value can also be qualified with the enum type that it resides in, `Direction.north`. For better interfacing to other programming languages, the fields of enum @@ -1254,10 +1269,11 @@ explicitly given is assigned the value of the previous field + 1. An explicit ordered enum can have *holes*: -.. code-block:: nim + ```nim type TokenType = enum a = 2, b = 4, c = 89 # holes are valid + ``` However, it is then not ordinal anymore, so it is impossible to use these enums as an index type for arrays. The procedures `inc`, `dec`, `succ` @@ -1268,14 +1284,14 @@ The compiler supports the built-in stringify operator `$` for enumerations. The stringify's result can be controlled by explicitly giving the string values to use: -.. code-block:: nim - + ```nim type MyEnum = enum valueA = (0, "my value A"), valueB = "value B", valueC = 2, valueD = (3, "abc") + ``` As can be seen from the example, it is possible to both specify a field's ordinal value and its string value by using a tuple. It is also @@ -1287,8 +1303,7 @@ as the last attempt. Only non-ambiguous symbols are added to this scope. But one can always access these via type qualification written as `MyEnum.value`: -.. code-block:: nim - + ```nim type MyEnum {.pure.} = enum valueA, valueB, valueC, valueD, amb @@ -1300,8 +1315,41 @@ as `MyEnum.value`: echo valueA # MyEnum.valueA echo amb # Error: Unclear whether it's MyEnum.amb or OtherEnum.amb echo MyEnum.amb # OK. + ``` -To implement bit fields with enums see `Bit fields <#set-type-bit-fields>`_ +Enum value names are overloadable, much like routines. If both of the enums +`T` and `U` have a member named `foo`, then the identifier `foo` corresponds +to a choice between `T.foo` and `U.foo`. During overload resolution, +the correct type of `foo` is decided from the context. If the type of `foo` is +ambiguous, a static error will be produced. + + ```nim test = "nim c $1" + + type + E1 = enum + value1, + value2 + E2 = enum + value1, + value2 = 4 + + const + Lookuptable = [ + E1.value1: "1", + # no need to qualify value2, known to be E1.value2 + value2: "2" + ] + + proc p(e: E1) = + # disambiguation in 'case' statements: + case e + of value1: echo "A" + of value2: echo "B" + + p value2 + ``` + +To implement bit fields with enums see [Bit fields]. String type @@ -1322,14 +1370,14 @@ Most native Nim types support conversion to strings with the special `$` proc. When calling the `echo` proc, for example, the built-in stringify operation for the parameter is called: -.. code-block:: nim - + ```nim echo 3 # calls `$` for `int` + ``` Whenever a user creates a specialized object, implementation of this procedure provides for `string` representation. -.. code-block:: nim + ```nim type Person = object name: string @@ -1341,6 +1389,7 @@ provides for `string` representation. # is natively an integer to convert it to # a string " years old." + ``` While `$p.name` can also be used, the `$` operation on a string does nothing. Note that we cannot rely on automatic conversion from an `int` to @@ -1350,18 +1399,18 @@ Strings are compared by their lexicographical order. All comparison operators are available. Strings can be indexed like arrays (lower bound is 0). Unlike arrays, they can be used in case statements: -.. code-block:: nim - + ```nim case paramStr(i) of "-v": incl(options, optVerbose) of "-h", "-?": incl(options, optHelp) else: write(stdout, "invalid command line option!\n") + ``` Per convention, all strings are UTF-8 strings, but this is not enforced. For example, when reading strings from binary files, they are merely a sequence of bytes. The index operation `s[i]` means the i-th *char* of `s`, not the -i-th *unichar*. The iterator `runes` from the `unicode module -`_ can be used for iteration over all Unicode characters. +i-th *unichar*. The iterator `runes` from the [unicode module](unicode.html) +can be used for iteration over all Unicode characters. cstring type @@ -1379,11 +1428,12 @@ A Nim `string` is implicitly convertible to `cstring` for convenience. If a Nim string is passed to a C-style variadic proc, it is implicitly converted to `cstring` too: -.. code-block:: nim + ```nim proc printf(formatstr: cstring) {.importc: "printf", varargs, header: "".} printf("This works %s", "as expected") + ``` Even though the conversion is implicit, it is not *safe*: The garbage collector does not consider a `cstring` to be a root and may collect the underlying @@ -1391,27 +1441,32 @@ memory. For this reason, the implicit conversion will be removed in future releases of the Nim compiler. Certain idioms like conversion of a `const` string to `cstring` are safe and will remain to be allowed. -A `$` proc is defined for cstrings that returns a string. Thus to get a nim +A `$` proc is defined for cstrings that returns a string. Thus, to get a nim string from a cstring: -.. code-block:: nim + ```nim var str: string = "Hello!" var cstr: cstring = str var newstr: string = $cstr + ``` `cstring` literals shouldn't be modified. -.. code-block:: nim + ```nim var x = cstring"literals" x[1] = 'A' # This is wrong!!! + ``` If the `cstring` originates from a regular memory (not read-only memory), it can be modified: -.. code-block:: nim + ```nim var x = "123456" var s: cstring = x s[0] = 'u' # This is ok + ``` + +`cstring` values may also be used in case statements like strings. Structured types ---------------- @@ -1445,8 +1500,7 @@ A sequence may be passed to a parameter that is of type *open array*. Example: -.. code-block:: nim - + ```nim type IntArray = array[0..5, int] # an array that is indexed with 0..5 IntSeq = seq[int] # a sequence of integers @@ -1457,6 +1511,7 @@ Example: y = @[1, 2, 3, 4, 5, 6] # the @ turns the array into a sequence let z = [1.0, 2, 3, 4] # the type of z is array[0..3, float] + ``` The lower bound of an array or sequence may be received by the built-in proc `low()`, the higher bound by `high()`. The length may be @@ -1474,8 +1529,7 @@ checks can be disabled via pragmas or invoking the compiler with the An array constructor can have explicit indexes for readability: -.. code-block:: nim - + ```nim type Values = enum valA, valB, valC @@ -1486,12 +1540,12 @@ An array constructor can have explicit indexes for readability: valB: "B", valC: "C" ] + ``` If an index is left out, `succ(lastIndex)` is used as the index value: -.. code-block:: nim - + ```nim type Values = enum valA, valB, valC, valD, valE @@ -1503,6 +1557,7 @@ value: valC: "C", "D", "e" ] + ``` @@ -1511,30 +1566,31 @@ Open arrays Often fixed size arrays turn out to be too inflexible; procedures should be able to deal with arrays of different sizes. The `openarray`:idx: type -allows this; it can only be used for parameters. Openarrays are always +allows this; it can only be used for parameters. Open arrays are always indexed with an `int` starting at position 0. The `len`, `low` and `high` operations are available for open arrays too. Any array with -a compatible base type can be passed to an openarray parameter, the index +a compatible base type can be passed to an open array parameter, the index type does not matter. In addition to arrays, sequences can also be passed to an open array parameter. -The openarray type cannot be nested: multidimensional openarrays are not +The `openarray` type cannot be nested: multidimensional open arrays are not supported because this is seldom needed and cannot be done efficiently. -.. code-block:: nim + ```nim proc testOpenArray(x: openArray[int]) = echo repr(x) testOpenArray([1,2,3]) # array[] testOpenArray(@[1,2,3]) # seq[] + ``` Varargs ------- -A `varargs` parameter is an openarray parameter that additionally -allows to pass a variable number of arguments to a procedure. The compiler +A `varargs` parameter is an open array parameter that additionally +allows a variable number of arguments to be passed to a procedure. The compiler converts the list of arguments to an array implicitly: -.. code-block:: nim + ```nim proc myWriteln(f: File, a: varargs[string]) = for s in items(a): write(f, s) @@ -1543,12 +1599,13 @@ converts the list of arguments to an array implicitly: myWriteln(stdout, "abc", "def", "xyz") # is transformed to: myWriteln(stdout, ["abc", "def", "xyz"]) + ``` -This transformation is only done if the varargs parameter is the +This transformation is only done if the `varargs` parameter is the last parameter in the procedure header. It is also possible to perform type conversions in this context: -.. code-block:: nim + ```nim proc myWriteln(f: File, a: varargs[string, `$`]) = for s in items(a): write(f, s) @@ -1556,7 +1613,8 @@ type conversions in this context: myWriteln(stdout, 123, "abc", 4.0) # is transformed to: - myWriteln(stdout, [$123, $"def", $4.0]) + myWriteln(stdout, [$123, $"abc", $4.0]) + ``` In this example `$` is applied to any argument that is passed to the parameter `a`. (Note that `$` applied to strings is a nop.) @@ -1564,21 +1622,23 @@ parameter `a`. (Note that `$` applied to strings is a nop.) Note that an explicit array constructor passed to a `varargs` parameter is not wrapped in another implicit array construction: -.. code-block:: nim + ```nim proc takeV[T](a: varargs[T]) = discard takeV([123, 2, 1]) # takeV's T is "int", not "array of int" + ``` `varargs[typed]` is treated specially: It matches a variable list of arguments of arbitrary type but *always* constructs an implicit array. This is required so that the builtin `echo` proc does what is expected: -.. code-block:: nim + ```nim proc echo*(x: varargs[typed, `$`]) {...} echo @[1, 2, 3] # prints "@[1, 2, 3]" and not "123" + ``` Unchecked arrays @@ -1588,20 +1648,22 @@ are not checked. This is often useful to implement customized flexibly sized arrays. Additionally, an unchecked array is translated into a C array of undetermined size: -.. code-block:: nim + ```nim type MySeq = object len, cap: int data: UncheckedArray[int] + ``` Produces roughly this C code: -.. code-block:: C + ```C typedef struct { NI len; NI cap; NI data[]; } MySeq; + ``` The base type of the unchecked array may not contain any GC'ed memory but this is currently not checked. @@ -1624,8 +1686,7 @@ must match the order of the tuple's definition. Different tuple-types are *equivalent* if they specify the same fields of the same type in the same order. The *names* of the fields also have to be the same. -.. code-block:: nim - + ```nim type Person = tuple[name: string, age: int] # type representing a person: # it consists of a name and an age. @@ -1638,15 +1699,17 @@ order. The *names* of the fields also have to be the same. assert Person is (string, int) assert (string, int) is Person assert Person isnot tuple[other: string, age: int] # `other` is a different identifier + ``` A tuple with one unnamed field can be constructed with the parentheses and a trailing comma: -.. code-block:: nim + ```nim proc echoUnaryTuple(a: (int,)) = echo a[0] echoUnaryTuple (1,) + ``` In fact, a trailing comma is allowed for every tuple construction. @@ -1657,11 +1720,12 @@ is compatible with the way the C compiler does it. For consistency with `object` declarations, tuples in a `type` section can also be defined with indentation instead of `[]`: -.. code-block:: nim + ```nim type Person = tuple # type representing a person name: string # a person consists of a name age: Natural # and an age + ``` Objects provide many features that tuples do not. Objects provide inheritance and the ability to hide fields from other modules. Objects with inheritance @@ -1669,7 +1733,7 @@ enabled have information about their type at runtime so that the `of` operator can be used to determine the object's type. The `of` operator is similar to the `instanceof` operator in Java. -.. code-block:: nim + ```nim type Person = object of RootObj name*: string # the * means that `name` is accessible from other modules @@ -1683,6 +1747,7 @@ the `instanceof` operator in Java. person: Person assert(student of Student) # is true assert(student of Person) # also true + ``` Object fields that should be visible from outside the defining module have to be marked by `*`. In contrast to tuples, different object types are @@ -1691,7 +1756,7 @@ Objects that have no ancestor are implicitly `final` and thus have no hidden type information. One can use the `inheritable` pragma to introduce new object roots apart from `system.RootObj`. -.. code-block:: nim + ```nim type Person = object # example of a final object name*: string @@ -1699,10 +1764,11 @@ introduce new object roots apart from `system.RootObj`. Student = ref object of Person # Error: inheritance only works with non-final objects id: int + ``` The assignment operator for tuples and objects copies each component. -The methods to override this copying behavior are described `here -`_. +The methods to override this copying behavior are described [here][type +bound operators]. Object construction @@ -1712,7 +1778,7 @@ Objects can also be created with an `object construction expression`:idx: that has the syntax `T(fieldA: valueA, fieldB: valueB, ...)` where `T` is an `object` type or a `ref object` type: -.. code-block:: nim + ```nim type Student = object name: string @@ -1724,6 +1790,7 @@ an `object` type or a `ref object` type: var a3 = (ref Student)(name: "Anton", age: 5) # not all fields need to be mentioned, and they can be mentioned out of order: var a4 = Student(age: 5) + ``` Note that, unlike tuples, objects require the field names along with their values. For a `ref object` type `system.new` is invoked implicitly. @@ -1738,8 +1805,7 @@ enumerated type used for runtime type flexibility, mirroring the concepts of An example: -.. code-block:: nim - + ```nim # This is an example of how an abstract syntax tree could be modelled in Nim type NodeKind = enum # the different node types @@ -1776,6 +1842,7 @@ An example: rightOp: Node(kind: nkInt, intVal: 2)) # valid: does not change the active object branch: x.kind = nkSub + ``` As can be seen from the example, an advantage to an object hierarchy is that no casting between different object types is needed. Yet, access to invalid @@ -1793,12 +1860,12 @@ corresponding discriminator value must be specified as a constant expression. Instead of changing the active object branch, replace the old object in memory with a new one completely: -.. code-block:: nim - + ```nim var x = Node(kind: nkAdd, leftOp: Node(kind: nkInt, intVal: 4), rightOp: Node(kind: nkInt, intVal: 2)) # change the node's contents: x[] = NodeObj(kind: nkString, strVal: "abc") + ``` Starting with version 0.20 `system.reset` cannot be used anymore to support @@ -1815,8 +1882,7 @@ valid for the chosen object branch. A small example: -.. code-block:: nim - + ```nim let unknownKind = nkSub # invalid: unsafe initialization because the kind field is not statically known: @@ -1833,6 +1899,7 @@ A small example: # also valid, since unknownKindBounded can only contain the values nkAdd or nkSub let unknownKindBounded = range[nkAdd..nkSub](unknownKind) z = Node(kind: unknownKindBounded, leftOp: Node(), rightOp: Node()) + ``` cast uncheckedAssign @@ -1840,9 +1907,7 @@ cast uncheckedAssign Some restrictions for case objects can be disabled via a `{.cast(uncheckedAssign).}` section: -.. code-block:: nim - :test: "nim c $1" - + ```nim test="nim c $1" type TokenKind* = enum strLit, intLit @@ -1867,6 +1932,7 @@ Some restrictions for case objects can be disabled via a `{.cast(uncheckedAssign # inside the 'cast' section it is allowed to assign to the 't.kind' field directly: t.kind = intLit + ``` Set type @@ -1883,7 +1949,7 @@ point to and modify the same location in memory (also called `aliasing`:idx:). Nim distinguishes between `traced`:idx: and `untraced`:idx: references. Untraced references are also called *pointers*. Traced references point to objects of a garbage-collected heap, untraced references point to -manually allocated objects or objects somewhere else in memory. Thus +manually allocated objects or objects somewhere else in memory. Thus, untraced references are *unsafe*. However, for certain low-level operations (accessing the hardware) untraced references are unavoidable. @@ -1894,14 +1960,13 @@ convertible to the `pointer` type. An empty subscript `[]` notation can be used to de-refer a reference, the `addr` procedure returns the address of an item. An address is always an untraced reference. -Thus the usage of `addr` is an *unsafe* feature. +Thus, the usage of `addr` is an *unsafe* feature. The `.` (access a tuple/object field operator) and `[]` (array/string/sequence index operator) operators perform implicit dereferencing operations for reference types: -.. code-block:: nim - + ```nim type Node = ref NodeObj NodeObj = object @@ -1913,16 +1978,18 @@ dereferencing operations for reference types: new(n) n.data = 9 # no need to write n[].data; in fact n[].data is highly discouraged! + ``` Automatic dereferencing can be performed for the first argument of a routine -call, but this is an experimental feature and is described `here -`_. +call, but this is an experimental feature and is described [here]( +manual_experimental.html#automatic-dereferencing). In order to simplify structural type checking, recursive tuples are not valid: -.. code-block:: nim + ```nim # invalid recursion type MyTuple = tuple[a: ref MyTuple] + ``` Likewise `T = ref T` is an invalid type. @@ -1930,17 +1997,17 @@ As a syntactical extension, `object` types can be anonymous if declared in a type section via the `ref object` or `ptr object` notations. This feature is useful if an object should only gain reference semantics: -.. code-block:: nim - + ```nim type Node = ref object le, ri: Node data: int + ``` To allocate a new traced object, the built-in procedure `new` has to be used. To deal with untraced memory, the procedures `alloc`, `dealloc` and -`realloc` can be used. The documentation of the `system `_ module +`realloc` can be used. The documentation of the [system](system.html) module contains further information. @@ -1957,20 +2024,20 @@ Dereferencing `nil` is an unrecoverable fatal runtime error (and not a panic). A successful dereferencing operation `p[]` implies that `p` is not nil. This can be exploited by the implementation to optimize code like: -.. code-block:: nim - + ```nim p[].field = 3 if p != nil: # if p were nil, `p[]` would have caused a crash already, # so we know `p` is always not nil here. action() + ``` Into: -.. code-block:: nim - + ```nim p[].field = 3 action() + ``` *Note*: This is not comparable to C's "undefined behavior" for @@ -1985,7 +2052,7 @@ traced references, strings, or sequences: in order to free everything properly, the built-in procedure `reset` has to be called before freeing the untraced memory manually: -.. code-block:: nim + ```nim type Data = tuple[x, y: int, s: string] @@ -2000,6 +2067,7 @@ memory manually: # free the memory: dealloc(d) + ``` Without the `reset` call the memory allocated for the `d.s` string would never be freed. The example also demonstrates two important features for @@ -2025,18 +2093,17 @@ an allowed value for a variable of a procedural type. Examples: -.. code-block:: nim - + ```nim proc printItem(x: int) = ... proc forEach(c: proc (x: int) {.cdecl.}) = ... forEach(printItem) # this will NOT compile because calling conventions differ + ``` -.. code-block:: nim - + ```nim type OnMouseMove = proc (x, y: int) {.closure.} @@ -2049,6 +2116,7 @@ Examples: # ok, 'onMouseMove' has the default calling convention, which is compatible # to 'closure': setOnMouseMove(onMouseMove) + ``` A subtle issue with procedural types is that the calling convention of the @@ -2060,52 +2128,52 @@ that expects a proc of the calling convention `closure`. Nim supports these `calling conventions`:idx:\: `nimcall`:idx: - is the default convention used for a Nim **proc**. It is the +: is the default convention used for a Nim **proc**. It is the same as `fastcall`, but only for C compilers that support `fastcall`. `closure`:idx: - is the default calling convention for a **procedural type** that lacks +: is the default calling convention for a **procedural type** that lacks any pragma annotations. It indicates that the procedure has a hidden implicit parameter (an *environment*). Proc vars that have the calling convention `closure` take up two machine words: One for the proc pointer and another one for the pointer to implicitly passed environment. `stdcall`:idx: - This is the stdcall convention as specified by Microsoft. The generated C +: This is the stdcall convention as specified by Microsoft. The generated C procedure is declared with the `__stdcall` keyword. `cdecl`:idx: - The cdecl convention means that a procedure shall use the same convention +: The cdecl convention means that a procedure shall use the same convention as the C compiler. Under Windows the generated C procedure is declared with the `__cdecl` keyword. `safecall`:idx: - This is the safecall convention as specified by Microsoft. The generated C +: This is the safecall convention as specified by Microsoft. The generated C procedure is declared with the `__safecall` keyword. The word *safe* refers to the fact that all hardware registers shall be pushed to the hardware stack. `inline`:idx: - The inline convention means the caller should not call the procedure, +: The inline convention means the caller should not call the procedure, but inline its code directly. Note that Nim does not inline, but leaves this to the C compiler; it generates `__inline` procedures. This is - only a hint for the compiler: it may completely ignore it and + only a hint for the compiler: it may completely ignore it, and it may inline procedures that are not marked as `inline`. `fastcall`:idx: - Fastcall means different things to different C compilers. One gets whatever +: Fastcall means different things to different C compilers. One gets whatever the C `__fastcall` means. `thiscall`:idx: - This is the thiscall calling convention as specified by Microsoft, used on +: This is the thiscall calling convention as specified by Microsoft, used on C++ class member functions on the x86 architecture. `syscall`:idx: - The syscall convention is the same as `__syscall`:c: in C. It is used for +: The syscall convention is the same as `__syscall`:c: in C. It is used for interrupts. `noconv`:idx: - The generated C code will not have any explicit calling convention and thus +: The generated C code will not have any explicit calling convention and thus use the C compiler's default calling convention. This is needed because Nim's default calling convention for procedures is `fastcall` to improve speed. @@ -2131,8 +2199,7 @@ reverse operation. A distinct type is an ordinal type if its base type is an ordinal type. -Modeling currencies -~~~~~~~~~~~~~~~~~~~~ +### Modeling currencies A distinct type can be used to model different physical `units`:idx: with a numerical base type, for example. The following example models currencies. @@ -2140,7 +2207,7 @@ numerical base type, for example. The following example models currencies. Different currencies should not be mixed in monetary calculations. Distinct types are a perfect tool to model different currencies: -.. code-block:: nim + ```nim type Dollar = distinct int Euro = distinct int @@ -2151,19 +2218,21 @@ types are a perfect tool to model different currencies: echo d + 12 # Error: cannot add a number with no unit and a `Dollar` + ``` Unfortunately, `d + 12.Dollar` is not allowed either, because `+` is defined for `int` (among others), not for `Dollar`. So a `+` for dollars needs to be defined: -.. code-block:: + ```nim proc `+` (x, y: Dollar): Dollar = result = Dollar(int(x) + int(y)) + ``` It does not make sense to multiply a dollar with a dollar, but with a number without unit; and the same holds for division: -.. code-block:: + ```nim proc `*` (x: Dollar, y: int): Dollar = result = Dollar(int(x) * y) @@ -2171,6 +2240,7 @@ number without unit; and the same holds for division: result = Dollar(x * int(y)) proc `div` ... + ``` This quickly gets tedious. The implementations are trivial and the compiler should not generate all this code only to optimize it away later - after all @@ -2178,21 +2248,20 @@ should not generate all this code only to optimize it away later - after all The pragma `borrow`:idx: has been designed to solve this problem; in principle, it generates the above trivial implementations: -.. code-block:: nim + ```nim proc `*` (x: Dollar, y: int): Dollar {.borrow.} proc `*` (x: int, y: Dollar): Dollar {.borrow.} proc `div` (x: Dollar, y: int): Dollar {.borrow.} + ``` The `borrow` pragma makes the compiler use the same implementation as the proc that deals with the distinct type's base type, so no code is generated. But it seems all this boilerplate code needs to be repeated for the `Euro` -currency. This can be solved with templates_. - -.. code-block:: nim - :test: "nim c $1" +currency. This can be solved with [templates]. + ```nim test = "nim c $1" template additive(typ: typedesc) = proc `+` *(x, y: typ): typ {.borrow.} proc `-` *(x, y: typ): typ {.borrow.} @@ -2221,12 +2290,13 @@ currency. This can be solved with templates_. defineCurrency(Dollar, int) defineCurrency(Euro, int) + ``` The borrow pragma can also be used to annotate the distinct type to allow certain builtin operations to be lifted: -.. code-block:: nim + ```nim type Foo = object a, b: int @@ -2239,18 +2309,18 @@ certain builtin operations to be lifted: # field access now valid bb.a = 90 bb.s = "abc" + ``` Currently, only the dot accessor can be borrowed in this way. -Avoiding SQL injection attacks -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +### Avoiding SQL injection attacks An SQL statement that is passed from Nim to an SQL database might be modeled as a string. However, using string templates and filling in the values is vulnerable to the famous `SQL injection attack`:idx:\: -.. code-block:: nim + ```nim import std/strutils proc query(db: DbHandle, statement: string) = ... @@ -2260,12 +2330,13 @@ values is vulnerable to the famous `SQL injection attack`:idx:\: db.query("SELECT FROM users WHERE name = '$1'" % username) # Horrible security hole, but the compiler does not mind! + ``` This can be avoided by distinguishing strings that contain SQL from strings that don't. Distinct types provide a means to introduce a new string type `SQL` that is incompatible with `string`: -.. code-block:: nim + ```nim type SQL = distinct string @@ -2276,13 +2347,14 @@ that don't. Distinct types provide a means to introduce a new string type db.query("SELECT FROM users WHERE name = '$1'" % username) # Static error: `query` expects an SQL string! + ``` It is an essential property of abstract types that they **do not** imply a subtype relation between the abstract type and its base type. Explicit type conversions from `string` to `SQL` are allowed: -.. code-block:: nim + ```nim import std/[strutils, sequtils] proc properQuote(s: string): SQL = @@ -2298,12 +2370,13 @@ conversions from `string` to `SQL` are allowed: result = SQL(string(frmt) % StrSeq(v)) db.query("SELECT FROM users WHERE name = '$1'".SQL % [username]) + ``` Now we have compile-time checking against SQL injection attacks. Since `"".SQL` is transformed to `SQL("")` no new syntax is needed for nice looking `SQL` string literals. The hypothetical `SQL` type actually -exists in the library as the `SqlQuery type `_ of -modules like `db_sqlite `_. +exists in the library as the [SqlQuery type](db_common.html#SqlQuery) of +modules like [db_sqlite](db_sqlite.html). Auto type @@ -2312,18 +2385,21 @@ Auto type The `auto` type can only be used for return types and parameters. For return types it causes the compiler to infer the type from the routine body: -.. code-block:: nim + ```nim proc returnsInt(): auto = 1984 + ``` For parameters it currently creates implicitly generic routines: -.. code-block:: nim + ```nim proc foo(a, b: auto) = discard + ``` Is the same as: -.. code-block:: nim + ```nim proc foo[T1, T2](a: T1, b: T2) = discard + ``` However, later versions of the language might change this to mean "infer the parameters' types from the body". Then the above `foo` would be rejected as @@ -2356,9 +2432,8 @@ If `A` is a subtype of `B` and `A` and `B` are `object` types then: - `ref A` is a subtype of `ref B` - `ptr A` is a subtype of `ptr B`. -**Note**: In later versions of the language the subtype relation might -be changed to *require* the pointer indirection in order to prevent -"object slicing". +**Note**: One of the above pointer-indirections is required for assignment from +a subtype to its parent type to prevent "object slicing". Convertible relation @@ -2367,8 +2442,7 @@ Convertible relation A type `a` is **implicitly** convertible to type `b` iff the following algorithm returns true: -.. code-block:: nim - + ```nim proc isImplicitlyConvertible(a, b: PType): bool = if isSubtype(a, b): return true @@ -2398,6 +2472,7 @@ algorithm returns true: result = b == cstring of proc: result = typeEquals(a, b) or compatibleParametersAndEffects(a, b) + ``` We used the predicate `typeEquals(a, b)` for the "type equality" property and the predicate `isSubtype(a, b)` for the "subtype relation". @@ -2417,7 +2492,7 @@ are signed integers or if both are unsigned integers. A type `a` is **explicitly** convertible to type `b` iff the following algorithm returns true: -.. code-block:: nim + ```nim proc isIntegralType(t: PType): bool = result = isOrdinal(t) or t.kind in {float, float32, float64} @@ -2429,11 +2504,12 @@ algorithm returns true: if b == distinct and typeEquals(b.baseType, a): return true if isIntegralType(a) and isIntegralType(b): return true if isSubtype(a, b) or isSubtype(b, a): return true + ``` The convertible relation can be relaxed by a user-defined type `converter`:idx:. -.. code-block:: nim + ```nim converter toInt(x: char): int = result = ord(x) var @@ -2446,6 +2522,7 @@ The convertible relation can be relaxed by a user-defined type # one can use the explicit form too x = chr.toInt echo x # => 97 + ``` The type conversion `T(a)` is an L-value if `a` is an L-value and `typeEqualsOrDistinct(T, typeof(a))` holds. @@ -2506,7 +2583,7 @@ algorithm returns true:: Some examples: -.. code-block:: nim + ```nim proc takesInt(x: int) = echo "int" proc takesInt[T](x: T) = echo "T" proc takesInt(x: int16) = echo "int16" @@ -2518,6 +2595,7 @@ Some examples: takesInt(y) # "int16" var z: range[0..4] = 0 takesInt(z) # "T" + ``` If this algorithm returns "ambiguous" further disambiguation is performed: @@ -2525,7 +2603,7 @@ If the argument `a` matches both the parameter type `f` of `p` and `g` of `q` via a subtyping relation, the inheritance depth is taken into account: -.. code-block:: nim + ```nim type A = object of RootObj B = object of A @@ -2547,18 +2625,20 @@ into account: # but this is ambiguous: pp(c, c) + ``` Likewise, for generic matches, the most specialized generic type (that still matches) is preferred: -.. code-block:: nim + ```nim proc gen[T](x: ref ref T) = echo "ref ref T" proc gen[T](x: ref T) = echo "ref T" proc gen[T](x: T) = echo "T" var ri: ref int gen(ri) # "ref T" + ``` Overloading based on 'var T' @@ -2569,7 +2649,7 @@ in addition to the ordinary type checking, the argument is checked to be an `l-value`:idx:. `var T` matches better than just `T` then. -.. code-block:: nim + ```nim proc sayHi(x: int): string = # matches a non-var int result = $x @@ -2584,6 +2664,7 @@ the argument is checked to be an `l-value`:idx:. sayHello(3) # 3 # 13 + ``` Lazy type resolution for untyped @@ -2597,10 +2678,11 @@ in overloading resolution, it's essential to have a way to pass unresolved expressions to a template or macro. This is what the meta-type `untyped` accomplishes: -.. code-block:: nim + ```nim template rem(x: untyped) = discard rem unresolvedExpression(undeclaredIdentifier) + ``` A parameter of type `untyped` always matches any argument (as long as there is any argument passed to it). @@ -2608,12 +2690,13 @@ any argument passed to it). But one has to watch out because other overloads might trigger the argument's resolution: -.. code-block:: nim + ```nim template rem(x: untyped) = discard proc rem[T](x: T) = discard # undeclared identifier: 'unresolvedExpression' rem unresolvedExpression(undeclaredIdentifier) + ``` `untyped` and `varargs[untyped]` are the only metatype that are lazy in this sense, the other metatypes `typed` and `typedesc` are not lazy. @@ -2622,7 +2705,7 @@ metatypes `typed` and `typedesc` are not lazy. Varargs matching ---------------- -See `Varargs <#types-varargs>`_. +See [Varargs]. iterable @@ -2632,7 +2715,7 @@ A called `iterator` yielding type `T` can be passed to a template or macro via a parameter typed as `untyped` (for unresolved expressions) or the type class `iterable` or `iterable[T]` (after type checking and overload resolution). -.. code-block:: nim + ```nim iterator iota(n: int): int = for i in 0..`_ -for details. +See [Constants and Constant Expressions] for details. Static statement/expression --------------------------- @@ -2928,15 +3031,14 @@ Static statement/expression A static statement/expression explicitly requires compile-time execution. Even some code that has side effects is permitted in a static block: -.. code-block:: - + ```nim static: echo "echo at compile time" + ``` `static` can also be used like a routine. -.. code-block:: nim - + ```nim proc getNum(a: int): int = a # Below calls "echo getNum(123)" at compile time. @@ -2946,10 +3048,10 @@ Even some code that has side effects is permitted in a static block: # Below call evaluates the "getNum(123)" at compile time, but its # result gets used at run time. echo static(getNum(123)) + ``` There are limitations on what Nim code can be executed at compile time; -see `Restrictions on Compile-Time Execution -<#restrictions-on-compileminustime-execution>`_ for details. +see [Restrictions on Compile-Time Execution] for details. It's a static error if the compiler cannot execute the block at compile time. @@ -2959,8 +3061,7 @@ If statement Example: -.. code-block:: nim - + ```nim var name = readLine(stdin) if name == "Andreas": @@ -2969,10 +3070,11 @@ Example: echo "Don't you have a name?" else: echo "Boring name..." + ``` The `if` statement is a simple way to make a branch in the control flow: The expression after the keyword `if` is evaluated, if it is true -the corresponding statements after the `:` are executed. Otherwise +the corresponding statements after the `:` are executed. Otherwise, the expression after the `elif` is evaluated (if there is an `elif` branch), if it is true the corresponding statements after the `:` are executed. This goes on until the last `elif`. If all @@ -2985,21 +3087,21 @@ corresponding *then* block. For visualization purposes the scopes have been enclosed in `{| |}` in the following example: -.. code-block:: nim + ```nim if {| (let m = input =~ re"(\w+)=\w+"; m.isMatch): echo "key ", m[0], " value ", m[1] |} elif {| (let m = input =~ re""; m.isMatch): echo "new m in this scope" |} else: {| echo "m not declared here" |} + ``` Case statement -------------- Example: -.. code-block:: nim - + ```nim let line = readline(stdin) case line of "delete-everything", "restart-computer": @@ -3015,6 +3117,7 @@ Example: echo "permission denied" of "go-for-a-walk": echo "please yourself" else: echo "unknown command" + ``` The `case` statement is similar to the `if` statement, but it represents @@ -3029,6 +3132,9 @@ This holds only for expressions of ordinal types. "All possible values" of `expr` are determined by `expr`'s type. To suppress the static error an `else: discard` should be used. +Only ordinal types, floats, strings and cstrings are allowed as values +in case statements. + For non-ordinal types, it is not possible to list every possible value and so these always require an `else` part. An exception to this rule is for the `string` type, which currently doesn't @@ -3043,7 +3149,7 @@ As a special semantic extension, an expression in an `of` branch of a case statement may evaluate to a set or array constructor; the set or array is then expanded into a list of its elements: -.. code-block:: nim + ```nim const SymChars: set[char] = {'a'..'z', 'A'..'Z', '\x80'..'\xFF'} @@ -3059,11 +3165,12 @@ expanded into a list of its elements: of 'a'..'z', 'A'..'Z', '\x80'..'\xFF', '_': echo "an identifier" of '0'..'9': echo "a number" else: echo "other" + ``` The `case` statement doesn't produce an l-value, so the following example won't work: -.. code-block:: nim + ```nim type Foo = ref object x: seq[string] @@ -3078,16 +3185,18 @@ won't work: var foo = Foo(x: @[]) foo.get_x().add("asd") + ``` This can be fixed by explicitly using `result` or `return`: -.. code-block:: nim + ```nim proc get_x(x: Foo): var seq[string] = case true of true: result = x.x else: result = x.x + ``` When statement @@ -3095,8 +3204,7 @@ When statement Example: -.. code-block:: nim - + ```nim when sizeof(int) == 2: echo "running on a 16 bit system!" elif sizeof(int) == 4: @@ -3105,6 +3213,7 @@ Example: echo "running on a 64 bit system!" else: echo "cannot happen!" + ``` The `when` statement is almost identical to the `if` statement with some exceptions: @@ -3129,7 +3238,7 @@ compile-time and the executable. Example: -.. code-block:: nim + ```nim proc someProcThatMayRunInCompileTime(): bool = when nimvm: # This branch is taken at compile time. @@ -3141,6 +3250,7 @@ Example: let rtValue = someProcThatMayRunInCompileTime() assert(ctValue == true) assert(rtValue == false) + ``` A `when nimvm` statement must meet the following requirements: @@ -3157,16 +3267,18 @@ Return statement Example: -.. code-block:: nim + ```nim return 40 + 2 + ``` The `return` statement ends the execution of the current procedure. It is only allowed in procedures. If there is an `expr`, this is syntactic sugar for: -.. code-block:: nim + ```nim result = expr return result + ``` `return` without an expression is a short notation for `return result` if @@ -3174,9 +3286,10 @@ the proc has a return type. The `result`:idx: variable is always the return value of the procedure. It is automatically declared by the compiler. As all variables, `result` is initialized to (binary) zero: -.. code-block:: nim + ```nim proc returnZero(): int = # implicitly returns 0 + ``` Yield statement @@ -3184,14 +3297,15 @@ Yield statement Example: -.. code-block:: nim + ```nim yield (1, 2, 3) + ``` The `yield` statement is used instead of the `return` statement in iterators. It is only valid in iterators. Execution is returned to the body of the for loop that called the iterator. Yield does not end the iteration process, but the execution is passed back to the iterator if the next iteration -starts. See the section about iterators (`Iterators and the for statement`_) +starts. See the section about iterators ([Iterators and the for statement]) for further information. @@ -3200,7 +3314,7 @@ Block statement Example: -.. code-block:: nim + ```nim var found = false block myblock: for i in 0..3: @@ -3209,6 +3323,7 @@ Example: found = true break myblock # leave the block, in this case both for-loops echo found + ``` The block statement is a means to group statements to a (named) `block`. Inside the block, the `break` statement is allowed to leave the block @@ -3221,8 +3336,9 @@ Break statement Example: -.. code-block:: nim + ```nim break + ``` The `break` statement is used to leave a block immediately. If `symbol` is given, it is the name of the enclosing block that is to be left. If it is @@ -3234,12 +3350,13 @@ While statement Example: -.. code-block:: nim + ```nim echo "Please tell me your password:" var pw = readLine(stdin) while pw != "12345": echo "Wrong password! Next try:" pw = readLine(stdin) + ``` The `while` statement is executed until the `expr` evaluates to false. @@ -3254,20 +3371,22 @@ A `continue` statement leads to the immediate next iteration of the surrounding loop construct. It is only allowed within a loop. A continue statement is syntactic sugar for a nested block: -.. code-block:: nim + ```nim while expr1: stmt1 continue stmt2 + ``` Is equivalent to: -.. code-block:: nim + ```nim while expr1: block myBlockName: stmt1 break myBlockName stmt2 + ``` Assembler statement @@ -3278,7 +3397,7 @@ by the unsafe `asm` statement. Identifiers in the assembler code that refer to Nim identifiers shall be enclosed in a special character which can be specified in the statement's pragmas. The default special character is `'\`'`: -.. code-block:: nim + ```nim {.push stackTrace:off.} proc addInt(a, b: int): int = # a in eax, and b in edx @@ -3290,10 +3409,11 @@ specified in the statement's pragmas. The default special character is `'\`'`: theEnd: """ {.pop.} + ``` If the GNU assembler is used, quotes and newlines are inserted automatically: -.. code-block:: nim + ```nim proc addInt(a, b: int): int = asm """ addl %%ecx, %%eax @@ -3303,10 +3423,11 @@ If the GNU assembler is used, quotes and newlines are inserted automatically: :"=a"(`result`) :"a"(`a`), "c"(`b`) """ + ``` Instead of: -.. code-block:: nim + ```nim proc addInt(a, b: int): int = asm """ "addl %%ecx, %%eax\n" @@ -3316,6 +3437,7 @@ Instead of: :"=a"(`result`) :"a"(`a`), "c"(`b`) """ + ``` Using statement --------------- @@ -3323,16 +3445,17 @@ Using statement The `using` statement provides syntactic convenience in modules where the same parameter names and types are used over and over. Instead of: -.. code-block:: nim + ```nim proc foo(c: Context; n: Node) = ... proc bar(c: Context; n: Node, counter: int) = ... proc baz(c: Context; n: Node) = ... + ``` One can tell the compiler about the convention that a parameter of name `c` should default to type `Context`, `n` should default to `Node` etc.: -.. code-block:: nim + ```nim using c: Context n: Node @@ -3346,6 +3469,7 @@ name `c` should default to type `Context`, `n` should default to # 'c' is inferred to be of the type 'Context' # 'n' is inferred to be of the type 'Node' # But 'x' and 'y' are of type 'int'. + ``` The `using` section uses the same indentation based grouping syntax as a `var` or `let` section. @@ -3364,8 +3488,9 @@ An `if` expression is almost like an if statement, but it is an expression. This feature is similar to *ternary operators* in other languages. Example: -.. code-block:: nim + ```nim var y = if x > 8: 9 else: 10 + ``` An if expression always results in a value, so the `else` part is required. `Elif` parts are also allowed. @@ -3380,7 +3505,7 @@ Case expression The `case` expression is again very similar to the case statement: -.. code-block:: nim + ```nim var favoriteFood = case animal of "dog": "bones" of "cat": "mice" @@ -3388,6 +3513,7 @@ The `case` expression is again very similar to the case statement: else: echo "I'm not sure what to serve, but everybody loves ice cream" "ice cream" + ``` 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 @@ -3401,23 +3527,25 @@ that uses the last expression under the block as the value. It is similar to the statement list expression, but the statement list expression does not open a new block scope. -.. code-block:: nim + ```nim let a = block: var fib = @[0, 1] for i in 0..10: fib.add fib[^1] + fib[^2] fib + ``` Table constructor ----------------- A table constructor is syntactic sugar for an array constructor: -.. code-block:: nim + ```nim {"key1": "value1", "key2", "key3": "value2"} # is the same as: [("key1", "value1"), ("key2", "value2"), ("key3", "value2")] + ``` The empty table can be written `{:}` (in contrast to the empty set @@ -3450,13 +3578,13 @@ can be used to convert from floating-point to integer or vice versa. Type conversion can also be used to disambiguate overloaded routines: -.. code-block:: nim - + ```nim proc p(x: int) = echo "int" proc p(x: string) = echo "string" let procVar = (proc(x: string))(p) procVar("a") + ``` Since operations on unsigned numbers wrap around and are unchecked so are type conversions to unsigned integers and between unsigned integers. The @@ -3479,19 +3607,21 @@ Type casts as if it would be of another type. Type casts are only needed for low-level programming and are inherently unsafe. -.. code-block:: nim + ```nim cast[int](x) + ``` The target type of a cast must be a concrete type, for instance, a target type that is a type class (which is non-concrete) would be invalid: -.. code-block:: nim + ```nim type Foo = int or float var x = cast[Foo](1) # Error: cannot cast to a non concrete type: 'Foo' + ``` Type casts should not be confused with *type conversions,* as mentioned in the prior section. Unlike type conversions, a type cast cannot change the underlying -bit pattern of the data being casted (aside from that the size of the target type +bit pattern of the data being cast (aside from that the size of the target type may differ from the source type). Casting resembles *type punning* in other languages or C++'s `reinterpret_cast`:cpp: and `bit_cast`:cpp: features. @@ -3506,8 +3636,7 @@ the address of variables. For easier interoperability with other compiled langua such as C, retrieving the address of a `let` variable, a parameter, or a `for` loop variable can be accomplished too: -.. code-block:: nim - + ```nim let t1 = "Hello" var t2 = t1 @@ -3518,15 +3647,17 @@ or a `for` loop variable can be accomplished too: # --> Hello # The following line also works echo repr(addr(t1)) + ``` The unsafeAddr operator ----------------------- The `unsafeAddr` operator is a deprecated alias for the `addr` operator: -.. code-block:: nim + ```nim let myArray = [1, 2, 3] foreignProcThatTakesAnAddr(unsafeAddr myArray) + ``` Procedures ========== @@ -3541,7 +3672,7 @@ until either the beginning of the parameter list, a semicolon separator, or an already typed parameter, is reached. The semicolon can be used to make separation of types and subsequent identifiers more distinct. -.. code-block:: nim + ```nim # Using only commas proc foo(a, b: int, c, d: bool): int @@ -3550,40 +3681,43 @@ separation of types and subsequent identifiers more distinct. # Will fail: a is untyped since ';' stops type propagation. proc foo(a; b: int; c, d: bool): int + ``` A parameter may be declared with a default value which is used if the caller does not provide a value for the argument. The value will be reevaluated every time the function is called. -.. code-block:: nim + ```nim # b is optional with 47 as its default value. proc foo(a: int, b: int = 47): int + ``` Just as the comma propagates the types from right to left until the first parameter or until a semicolon is hit, it also propagates the default value starting from the parameter declared with it. -.. code-block:: nim + ```nim # Both a and b are optional with 47 as their default values. proc foo(a, b: int = 47): int + ``` Parameters can be declared mutable and so allow the proc to modify those arguments, by using the type modifier `var`. -.. code-block:: nim + ```nim # "returning" a value to the caller through the 2nd argument # Notice that the function uses no actual return value at all (ie void) proc foo(inp: int, outp: var int) = outp = inp + 47 + ``` -If the proc declaration has no body, it is a `forward`:idx: declaration. If the -proc returns a value, the procedure body can access an implicitly declared +If the proc declaration doesn't have a body, it is a `forward`:idx: declaration. +If the proc returns a value, the procedure body can access an implicitly declared variable named `result`:idx: that represents the return value. Procs can be overloaded. The overloading resolution algorithm determines which proc is the best match for the arguments. Example: -.. code-block:: nim - + ```nim proc toLower(c: char): char = # toLower for characters if c in {'A'..'Z'}: result = chr(ord(c) + (ord('a') - ord('A'))) @@ -3594,10 +3728,11 @@ best match for the arguments. Example: result = newString(len(s)) for i in 0..len(s) - 1: result[i] = toLower(s[i]) # calls toLower for characters; no recursion! + ``` -Calling a procedure can be done in many different ways: +Calling a procedure can be done in many ways: -.. code-block:: nim + ```nim proc callme(x, y: int, s: string = "", c: char, b: bool = false) = ... # call with positional arguments # parameter bindings: @@ -3608,16 +3743,18 @@ Calling a procedure can be done in many different ways: callme(c='\t', y=1, x=0) # (x=0, y=1, s="", c='\t', b=false) # call as a command statement: no () needed: callme 0, 1, "abc", '\t' # (x=0, y=1, s="abc", c='\t', b=false) + ``` A procedure may call itself recursively. `Operators`:idx: are procedures with a special operator symbol as identifier: -.. code-block:: nim + ```nim proc `$` (x: int): string = # converts an integer to a string; this is a prefix operator. result = intToStr(x) + ``` Operators with one parameter are prefix operators, operators with two parameters are infix operators. (However, the parser distinguishes these from @@ -3628,12 +3765,13 @@ grammar explicitly. Any operator can be called like an ordinary proc with the \`opr\` notation. (Thus an operator can have more than two parameters): -.. code-block:: nim + ```nim proc `*+` (a, b, c: int): int = # Multiply and add result = a * b + c assert `*+`(3, 4, 6) == `+`(`*`(a, b), c) + ``` Export marker @@ -3642,8 +3780,7 @@ Export marker If a declared symbol is marked with an `asterisk`:idx: it is exported from the current module: -.. code-block:: nim - + ```nim proc exportedEcho*(s: string) = echo s proc `*`*(a: string; b: int): string = result = newStringOfCap(a.len * b) @@ -3654,6 +3791,7 @@ current module: type ExportedType* = object exportedField*: int + ``` Method call syntax @@ -3666,12 +3804,12 @@ there are no remaining arguments: `obj.len` (instead of `len(obj)`). This method call syntax is not restricted to objects, it can be used to supply any type of first argument for procedures: -.. code-block:: nim - + ```nim echo "abc".len # is the same as echo len "abc" echo "abc".toUpper() echo {'a', 'b', 'c'}.card stdout.writeLine("Hallo") # the same as writeLine(stdout, "Hallo") + ``` Another way to look at the method call syntax is that it provides the missing postfix notation. @@ -3680,8 +3818,7 @@ The method call syntax conflicts with explicit generic instantiations: `p[T](x)` cannot be written as `x.p[T]` because `x.p[T]` is always parsed as `(x.p)[T]`. -See also: `Limitations of the method call syntax -<#templates-limitations-of-the-method-call-syntax>`_. +See also: [Limitations of the method call syntax]. The `[: ]` notation has been designed to mitigate this issue: `x.p[:T]` is rewritten by the parser to `p[T](x)`, `x.p[:T](y)` is rewritten to @@ -3695,7 +3832,7 @@ Nim has no need for *get-properties*: Ordinary get-procedures that are called with the *method call syntax* achieve the same. But setting a value is different; for this, a special setter syntax is needed: -.. code-block:: nim + ```nim # Module asocket type Socket* = ref object of RootObj @@ -3714,24 +3851,26 @@ different; for this, a special setter syntax is needed: ## `host` because the builtin dot access is preferred if it is ## available: s.host + ``` -.. code-block:: nim + ```nim # module B import asocket var s: Socket new s s.host = 34 # same as `host=`(s, 34) + ``` A proc defined as `f=` (with the trailing `=`) is called a `setter`:idx:. A setter can be called explicitly via the common backticks notation: -.. code-block:: nim - + ```nim proc `f=`(x: MyObject; value: string) = discard `f=`(myObject, "value") + ``` `f=` can be called implicitly in the pattern @@ -3752,7 +3891,7 @@ means `echo f 1, f 2` is parsed as `echo(f(1), f(2))` and not as `echo(f(1, f(2)))`. The method call syntax may be used to provide one more argument in this case: -.. code-block:: nim + ```nim proc optarg(x: int, y: int = 0): int = x + y proc singlearg(x: int): int = 20*x @@ -3762,9 +3901,10 @@ more argument in this case: let x = optarg(1, optarg 8) # traditional procedure call with 2 arguments let y = 1.optarg optarg 8 # same thing as above, w/o the parenthesis assert x == y + ``` The command invocation syntax also can't have complex expressions as arguments. -For example: (`anonymous procs <#procedures-anonymous-procs>`_), `if`, +For example: [anonymous procedures], `if`, `case` or `try`. Function calls with no arguments still need () to distinguish between a call and the function itself as a first-class value. @@ -3781,13 +3921,12 @@ the closure and its enclosing scope (i.e. any modifications made to them are visible in both places). The closure environment may be allocated on the heap or on the stack if the compiler determines that this would be safe. -Creating closures in loops -~~~~~~~~~~~~~~~~~~~~~~~~~~ +### Creating closures in loops Since closures capture local variables by reference it is often not wanted -behavior inside loop bodies. See `closureScope -`_ and `capture -`_ for details on how to change this behavior. +behavior inside loop bodies. See [closureScope]( +system.html#closureScope.t,untyped) and [capture]( +sugar.html#capture.m,varargs[typed],untyped) for details on how to change this behavior. Anonymous procedures -------------------- @@ -3795,15 +3934,16 @@ Anonymous procedures Unnamed procedures can be used as lambda expressions to pass into other procedures: -.. code-block:: nim + ```nim var cities = @["Frankfurt", "Tokyo", "New York", "Kyiv"] cities.sort(proc (x, y: string): int = cmp(x.len, y.len)) + ``` Procs as expressions can appear both as nested procs and inside top-level -executable code. The `sugar `_ module contains the `=>` macro +executable code. The [sugar](sugar.html) module contains the `=>` macro which enables a more succinct syntax for anonymous procedures resembling lambdas as they are in languages like JavaScript, C#, etc. @@ -3814,7 +3954,7 @@ As a special convenience notation that keeps most elements of a regular proc expression, the `do` keyword can be used to pass anonymous procedures to routines: -.. code-block:: nim + ```nim var cities = @["Frankfurt", "Tokyo", "New York", "Kyiv"] sort(cities) do (x, y: string) -> int: @@ -3823,8 +3963,9 @@ anonymous procedures to routines: # Less parentheses using the method plus command syntax: cities = cities.map do (x: string) -> string: "City of " & x + ``` -`do` is written after the parentheses enclosing the regular proc params. +`do` is written after the parentheses enclosing the regular proc parameters. The proc expression represented by the `do` block is appended to the routine call as the last argument. In calls using the command syntax, the `do` block will bind to the immediately preceding expression rather than the command call. @@ -3834,28 +3975,31 @@ however `do` without parameters or pragmas is treated as a normal statement list. This allows macros to receive both indented statement lists as an argument in inline calls, as well as a direct mirror of Nim's routine syntax. -.. code-block:: nim + ```nim # Passing a statement list to an inline macro: macroResults.add quote do: if not `ex`: echo `info`, ": Check failed: ", `expString` - + # Processing a routine definition in a macro: rpc(router, "add") do (a, b: int) -> int: result = a + b + ``` Func ---- The `func` keyword introduces a shortcut for a `noSideEffect`:idx: proc. -.. code-block:: nim + ```nim func binarySearch[T](a: openArray[T]; elem: T): int + ``` Is short for: -.. code-block:: nim + ```nim proc binarySearch[T](a: openArray[T]; elem: T): int {.noSideEffect.} + ``` @@ -3869,11 +4013,11 @@ Type bound operators A type bound operator is a `proc` or `func` whose name starts with `=` but isn't an operator (i.e. containing only symbols, such as `==`). These are unrelated to setters -(see `properties `_), which instead end in `=`. +(see [Properties]), which instead end in `=`. A type bound operator declared for a type applies to the type regardless of whether the operator is in scope (including if it is private). -.. code-block:: nim + ```nim # foo.nim: var witness* = 0 type Foo[T] = object @@ -3893,6 +4037,7 @@ the operator is in scope (including if it is private). doAssert witness == 2 # will still be called upon exiting scope doAssert witness == 3 + ``` Type bound operators are: `=destroy`, `=copy`, `=sink`, `=trace`, `=deepcopy`. @@ -3910,7 +4055,7 @@ This also means that one cannot override `deepCopy` for both `ptr T` and used for one pointer type. For more details on some of those procs, see -`Lifetime-tracking hooks `_. +[Lifetime-tracking hooks](destructors.html#lifetimeminustracking-hooks). Nonoverloadable builtins ------------------------ @@ -3921,9 +4066,10 @@ simplicity (they require specialized semantic checking):: declared, defined, definedInScope, compiles, sizeof, is, shallowCopy, getAst, astToStr, spawn, procCall -Thus they act more like keywords than like ordinary identifiers; unlike a +Thus, they act more like keywords than like ordinary identifiers; unlike a keyword however, a redefinition may `shadow`:idx: the definition in -the system_ module. From this list the following should not be written in dot +the [system](system.html) module. +From this list the following should not be written in dot notation `x.f` since `x` cannot be type-checked before it gets passed to `f`:: @@ -3934,7 +4080,7 @@ Var parameters -------------- The type of a parameter may be prefixed with the `var` keyword: -.. code-block:: nim + ```nim proc divmod(a, b: int; res, remainder: var int) = res = a div b remainder = a mod b @@ -3945,6 +4091,7 @@ The type of a parameter may be prefixed with the `var` keyword: divmod(8, 5, x, y) # modifies x and y assert x == 1 assert y == 3 + ``` In the example, `res` and `remainder` are `var parameters`. Var parameters can be modified by the procedure and the changes are @@ -3952,7 +4099,7 @@ visible to the caller. The argument passed to a var parameter has to be an l-value. Var parameters are implemented as hidden pointers. The above example is equivalent to: -.. code-block:: nim + ```nim proc divmod(a, b: int; res, remainder: ptr int) = res[] = a div b remainder[] = a mod b @@ -3962,11 +4109,12 @@ above example is equivalent to: divmod(8, 5, addr(x), addr(y)) assert x == 1 assert y == 3 + ``` In the examples, var parameters or pointers are used to provide two return values. This can be done in a cleaner way by returning a tuple: -.. code-block:: nim + ```nim proc divmod(a, b: int): tuple[res, remainder: int] = (a div b, a mod b) @@ -3974,13 +4122,15 @@ return values. This can be done in a cleaner way by returning a tuple: assert t.res == 1 assert t.remainder == 3 + ``` One can use `tuple unpacking`:idx: to access the tuple's fields: -.. code-block:: nim + ```nim var (x, y) = divmod(8, 5) # tuple unpacking assert x == 1 assert y == 3 + ``` **Note**: `var` parameters are never necessary for efficient parameter @@ -3994,7 +4144,7 @@ Var return type A proc, converter, or iterator may return a `var` type which means that the returned value is an l-value and can be modified by the caller: -.. code-block:: nim + ```nim var g = 0 proc writeAccessToG(): var int = @@ -4002,36 +4152,39 @@ returned value is an l-value and can be modified by the caller: writeAccessToG() = 6 assert g == 6 + ``` It is a static error if the implicitly introduced pointer could be used to access a location beyond its lifetime: -.. code-block:: nim + ```nim proc writeAccessToG(): var int = var g = 0 result = g # Error! + ``` For iterators, a component of a tuple return type can have a `var` type too: -.. code-block:: nim + ```nim iterator mpairs(a: var seq[string]): tuple[key: int, val: var string] = for i in 0..a.high: yield (i, a[i]) + ``` In the standard library every name of a routine that returns a `var` type starts with the prefix `m` per convention. -.. include:: manual/var_t_return.rst +.. include:: manual/var_t_return.md -Future directions -~~~~~~~~~~~~~~~~~ +### Future directions Later versions of Nim can be more precise about the borrowing rule with a syntax like: -.. code-block:: nim + ```nim proc foo(other: Y; container: var X): var T from container + ``` Here `var T from container` explicitly exposes that the location is derived from the second parameter (called @@ -4057,7 +4210,7 @@ receives a hidden mutable parameter representing `result`. Informally: -.. code-block:: nim + ```nim proc p(): BigT = ... var x = p() @@ -4069,6 +4222,7 @@ Informally: var x; p(x) p(x) + ``` Let `T`'s be `p`'s return type. NRVO applies for `T` @@ -4078,8 +4232,7 @@ in other words, it applies for "big" structures. If `p` can raise an exception, NRVO applies regardless. This can produce observable differences in behavior: -.. code-block:: nim - + ```nim type BigT = array[16, int] @@ -4096,18 +4249,19 @@ observable differences in behavior: doAssert x == [0, 1, 2, 3, 4, 5, 6, 7, 0, 0, 0, 0, 0, 0, 0, 0] main() + ``` -However, the current implementation produces a warning in these cases. -There are different ways to deal with this warning: - -1. Disable the warning via `{.push warning[ObservableStores]: off.}` ... `{.pop.}`. - Then one may need to ensure that `p` only raises *before* any stores to `result` - happen. - -2. One can use a temporary helper variable, for example instead of `x = p(8)` - use `let tmp = p(8); x = tmp`. +The compiler can produce a warning in these cases, however this behavior is +turned off by default. It can be enabled for a section of code via the +`warning[ObservableStores]` and `push`/`pop` pragmas. Take the above code +as an example: + ```nim + {.push warning[ObservableStores]: on.} + main() + {.pop.} + ``` Overloading of the subscript operator ------------------------------------- @@ -4122,7 +4276,7 @@ Procedures always use static dispatch. Methods use dynamic dispatch. For dynamic dispatch to work on an object it should be a reference type. -.. code-block:: nim + ```nim type Expression = ref object of RootObj ## abstract base class for an expression Literal = ref object of Expression @@ -4150,6 +4304,7 @@ type. result.b = b echo eval(newPlus(newPlus(newLit(1), newLit(2)), newLit(4))) + ``` In the example the constructors `newLit` and `newPlus` are procs because they should use static binding, but `eval` is a method because it @@ -4174,15 +4329,13 @@ Multi-methods In a multi-method, all parameters that have an object type are used for the dispatching: -.. code-block:: nim - :test: "nim c --multiMethods:on $1" - + ```nim test = "nim c --multiMethods:on $1" type Thing = ref object of RootObj Unit = ref object of Thing x: int - method collide(a, b: Thing) {.inline.} = + method collide(a, b: Thing) {.base, inline.} = quit "to override!" method collide(a: Thing, b: Unit) {.inline.} = @@ -4195,6 +4348,7 @@ dispatching: new a new b collide(a, b) # output: 2 + ``` Inhibit dynamic method resolution via procCall ----------------------------------------------- @@ -4203,9 +4357,7 @@ Dynamic method resolution can be inhibited via the builtin `system.procCall`:idx This is somewhat comparable to the `super`:idx: keyword that traditional OOP languages offer. -.. code-block:: nim - :test: "nim c $1" - + ```nim test = "nim c $1" type Thing = ref object of RootObj Unit = ref object of Thing @@ -4218,6 +4370,7 @@ languages offer. # Call the base method: procCall m(Thing(a)) echo "1" + ``` Iterators and the for statement @@ -4240,7 +4393,7 @@ reached, the data is bound to the `for` loop variables and control continues in the body of the `for` loop. The iterator's local variables and execution state are automatically saved between calls. Example: -.. code-block:: nim + ```nim # this definition exists in the system module iterator items*(a: string): char {.inline.} = var i = 0 @@ -4250,15 +4403,17 @@ state are automatically saved between calls. Example: for ch in items("hello world"): # `ch` is an iteration variable echo ch + ``` -The compiler generates code as if the programmer would have written this: +The compiler generates code as if the programmer had written this: -.. code-block:: nim + ```nim var i = 0 while i < len(a): var ch = a[i] echo ch inc(i) + ``` If the iterator yields a tuple, there can be as many iteration variables as there are components in the tuple. The i'th iteration variable's type is @@ -4270,10 +4425,11 @@ Implicit items/pairs invocations If the for loop expression `e` does not denote an iterator and the for loop has exactly 1 variable, the for loop expression is rewritten to `items(e)`; -ie. an `items` iterator is implicitly invoked: +i.e. an `items` iterator is implicitly invoked: -.. code-block:: nim + ```nim for x in [1,2,3]: echo x + ``` If the for loop has exactly 2 variables, a `pairs` iterator is implicitly invoked. @@ -4302,7 +4458,7 @@ templates, macros, and other inline iterators. In contrast to that, a `closure iterator`:idx: can be passed around more freely: -.. code-block:: nim + ```nim iterator count0(): int {.closure.} = yield 0 @@ -4317,19 +4473,17 @@ In contrast to that, a `closure iterator`:idx: can be passed around more freely: invoke(count0) invoke(count2) + ``` Closure iterators and inline iterators have some restrictions: 1. For now, a closure iterator cannot be executed at compile time. 2. `return` is allowed in a closure iterator but not in an inline iterator (but rarely useful) and ends the iteration. -3. Neither inline nor closure iterators can be (directly)* recursive. +3. Inline iterators cannot be recursive. 4. Neither inline nor closure iterators have the special `result` variable. 5. Closure iterators are not supported by the JS backend. -(*) Closure iterators can be co-recursive with a factory proc which results -in similar syntax to a recursive iterator. More details follow. - Iterators that are neither marked `{.closure.}` nor `{.inline.}` explicitly default to being inline, but this may change in future versions of the implementation. @@ -4338,7 +4492,7 @@ The `iterator` type is always of the calling convention `closure` implicitly; the following example shows how to use iterators to implement a `collaborative tasking`:idx: system: -.. code-block:: nim + ```nim # simple tasking: type Task = iterator (ticker: int) @@ -4368,15 +4522,16 @@ a `collaborative tasking`:idx: system: inc ticker runTasks(a1, a2) + ``` The builtin `system.finished` can be used to determine if an iterator has finished its operation; no exception is raised on an attempt to invoke an iterator that has already finished its work. -Note that `system.finished` is error prone to use because it only returns +Note that `system.finished` is error-prone to use because it only returns `true` one iteration after the iterator has finished: -.. code-block:: nim + ```nim iterator mycount(a, b: int): int {.closure.} = var x = a while x <= b: @@ -4392,15 +4547,17 @@ Note that `system.finished` is error prone to use because it only returns 2 3 0 + ``` -Instead this code has to be used: +Instead, this code has to be used: -.. code-block:: nim + ```nim var c = mycount # instantiate the iterator while true: let value = c(1, 3) if finished(c): break # and discard 'value'! echo value + ``` It helps to think that the iterator actually returns a pair `(value, done)` and `finished` is used to access the hidden `done` @@ -4411,7 +4568,7 @@ Closure iterators are *resumable functions* and so one has to provide the arguments to every call. To get around this limitation one can capture parameters of an outer factory proc: -.. code-block:: nim + ```nim proc mycount(a, b: int): iterator (): int = result = iterator (): int = var x = a @@ -4423,10 +4580,11 @@ parameters of an outer factory proc: for f in foo(): echo f + ``` The call can be made more like an inline iterator with a for loop macro: -.. code-block:: nim + ```nim import std/macros macro toItr(x: ForLoopStmt): untyped = let expr = x[0] @@ -4440,8 +4598,9 @@ The call can be made more like an inline iterator with a for loop macro: for f in toItr(mycount(1, 4)): # using early `proc mycount` echo f + ``` -Because of full backend function call aparatus involvment, closure iterator +Because of full backend function call apparatus involvement, closure iterator invocation is typically higher cost than inline iterators. Adornment by a macro wrapper at the call site like this is a possibly useful reminder. @@ -4449,7 +4608,7 @@ The factory `proc`, as an ordinary procedure, can be recursive. The above macro allows such recursion to look much like a recursive iterator would. For example: -.. code-block:: nim + ```nim proc recCountDown(n: int): iterator(): int = result = iterator(): int = if n > 0: @@ -4459,27 +4618,29 @@ would. For example: for i in toItr(recCountDown(6)): # Emits: 6 5 4 3 2 1 echo i + ``` -See also see `iterable <#overloading-resolution-iterable>`_ for passing iterators to templates and macros. +See also [iterable] for passing iterators to templates and macros. Converters ========== A converter is like an ordinary proc except that it enhances -the "implicitly convertible" type relation (see `Convertible relation`_): +the "implicitly convertible" type relation (see [Convertible relation]): -.. code-block:: nim + ```nim # bad style ahead: Nim is not C. converter toBool(x: int): bool = x != 0 if 4: echo "compiles" + ``` A converter can also be explicitly invoked for improved readability. Note that implicit converter chaining is not supported: If there is a converter from -type A to type B and from type B to type C the implicit conversion from A to C +type A to type B and from type B to type C, the implicit conversion from A to C is not provided. @@ -4488,7 +4649,7 @@ Type sections Example: -.. code-block:: nim + ```nim type # example demonstrating mutually recursive types Node = ref object # an object managed by the garbage collector (ref) le, ri: Node # left and right subtrees @@ -4498,6 +4659,7 @@ Example: name: string # the symbol's name line: int # the line the symbol was declared in code: Node # the symbol's abstract syntax tree + ``` A type section begins with the `type` keyword. It contains multiple type definitions. A type definition binds a type to a name. Type definitions @@ -4515,7 +4677,7 @@ Try statement Example: -.. code-block:: nim + ```nim # read the first two lines of a text file that should contain numbers # and tries to add them var @@ -4533,6 +4695,7 @@ Example: echo "Unknown exception!" finally: close(f) + ``` The statements after the `try` are executed in sequential order unless @@ -4561,19 +4724,21 @@ Try can also be used as an expression; the type of the `try` branch then needs to fit the types of `except` branches, but the type of the `finally` branch always has to be `void`: -.. code-block:: nim + ```nim from std/strutils import parseInt let x = try: parseInt("133a") except: -1 finally: echo "hi" + ``` To prevent confusing code there is a parsing limitation; if the `try` follows a `(` it has to be written as a one liner: -.. code-block:: nim + ```nim let x = (try: parseInt("133a") except: -1) + ``` Except clauses @@ -4582,59 +4747,65 @@ Except clauses Within an `except` clause it is possible to access the current exception using the following syntax: -.. code-block:: nim + ```nim try: # ... except IOError as e: # Now use "e" echo "I/O error: " & e.msg + ``` Alternatively, it is possible to use `getCurrentException` to retrieve the exception that has been raised: -.. code-block:: nim + ```nim try: # ... except IOError: let e = getCurrentException() # Now use "e" + ``` Note that `getCurrentException` always returns a `ref Exception` type. If a variable of the proper type is needed (in the example above, `IOError`), one must convert it explicitly: -.. code-block:: nim + ```nim try: # ... except IOError: let e = (ref IOError)(getCurrentException()) # "e" is now of the proper type + ``` However, this is seldom needed. The most common case is to extract an error message from `e`, and for such situations, it is enough to use `getCurrentExceptionMsg`: -.. code-block:: nim + ```nim try: # ... except: echo getCurrentExceptionMsg() + ``` Custom exceptions ----------------- It is possible to create custom exceptions. A custom exception is a custom type: -.. code-block:: nim + ```nim type LoadError* = object of Exception + ``` Ending the custom exception's name with `Error` is recommended. Custom exceptions can be raised just like any other exception, e.g.: -.. code-block:: nim + ```nim raise newException(LoadError, "Failed to load data") + ``` Defer statement --------------- @@ -4643,23 +4814,20 @@ Instead of a `try finally` statement a `defer` statement can be used, which avoids lexical nesting and offers more flexibility in terms of scoping as shown below. -Any statements following the `defer` in the current block will be considered -to be in an implicit try block: - -.. code-block:: nim - :test: "nim c $1" +Any statements following the `defer` will be considered +to be in an implicit try block in the current block: + ```nim test = "nim c $1" proc main = var f = open("numbers.txt", fmWrite) defer: close(f) f.write "abc" f.write "def" + ``` Is rewritten to: -.. code-block:: nim - :test: "nim c $1" - + ```nim test = "nim c $1" proc main = var f = open("numbers.txt") try: @@ -4667,13 +4835,12 @@ Is rewritten to: f.write "def" finally: close(f) + ``` When `defer` is at the outermost scope of a template/macro, its scope extends -to the block where the template is called from: - -.. code-block:: nim - :test: "nim c $1" +to the block where the template/macro is called from: + ```nim test = "nim c $1" template safeOpenDefer(f, path) = var f = open(path, fmWrite) defer: close(f) @@ -4694,6 +4861,7 @@ to the block where the template is called from: try: f.write "abc" # adds a lexical scope finally: close(f) + ``` Top-level `defer` statements are not supported since it's unclear what such a statement should refer to. @@ -4704,8 +4872,9 @@ Raise statement Example: -.. code-block:: nim + ```nim raise newException(IOError, "IO failed") + ``` Apart from built-in operations like array indexing, memory allocation, etc. the `raise` statement is the only way to raise an exception. @@ -4721,7 +4890,7 @@ exception. Exception hierarchy ------------------- -The exception tree is defined in the `system `_ module. +The exception tree is defined in the [system](system.html) module. Every exception inherits from `system.Exception`. Exceptions that indicate programming bugs inherit from `system.Defect` (which is a subtype of `Exception`) and are strictly speaking not catchable as they can also be mapped to an operation @@ -4739,9 +4908,7 @@ It is possible to raise/catch imported C++ exceptions. Types imported using `importcpp` can be raised or caught. Exceptions are raised by value and caught by reference. Example: -.. code-block:: nim - :test: "nim cpp -r $1" - + ```nim test = "nim cpp -r $1" type CStdException {.importcpp: "std::exception", header: "", inheritable.} = object ## does not inherit from `RootObj`, so we use `inheritable` instead @@ -4772,6 +4939,7 @@ caught by reference. Example: doAssert $b == "foo3" fn() + ``` **Note:** `getCurrentException()` and `getCurrentExceptionMsg()` are not available for imported exceptions from C++. One needs to use the `except ImportedException as x:` syntax @@ -4793,31 +4961,28 @@ Nim supports exception tracking. The `raises`:idx: pragma can be used to explicitly define which exceptions a proc/iterator/method/converter is allowed to raise. The compiler verifies this: -.. code-block:: nim - :test: "nim c $1" - + ```nim test = "nim c $1" proc p(what: bool) {.raises: [IOError, OSError].} = if what: raise newException(IOError, "IO") else: raise newException(OSError, "OS") + ``` An empty `raises` list (`raises: []`) means that no exception may be raised: -.. code-block:: nim + ```nim proc p(): bool {.raises: [].} = try: unsafeCall() result = true except: result = false + ``` A `raises` list can also be attached to a proc type. This affects type compatibility: -.. code-block:: nim - :test: "nim c $1" - :status: 1 - + ```nim test = "nim c $1" status = 1 type Callback = proc (s: string) {.raises: [IOError].} var @@ -4827,6 +4992,7 @@ compatibility: raise newException(OSError, "OS") c = p # type error + ``` For a routine `p`, the compiler uses inference rules to determine the set of @@ -4840,7 +5006,7 @@ possibly raised exceptions; the algorithm operates on `p`'s call graph: The call is optimistically assumed to have no effect. Rule 2 compensates for this case. 2. Every expression `e` of some proc type within a call that is passed to parameter - marked as `.effectsOf` is assumed to be called indirectly and thus + marked as `.effectsOf` of proc `p` is assumed to be called indirectly and thus its raises list is added to `p`'s raises list. 3. Every call to a proc `q` which has an unknown body (due to a forward declaration) is assumed to @@ -4858,18 +5024,18 @@ Exceptions inheriting from `system.Defect` are not tracked with the `.raises: []` exception tracking mechanism. This is more consistent with the built-in operations. The following code is valid: -.. code-block:: nim - + ```nim proc mydiv(a, b): int {.raises: [].} = a div b # can raise an DivByZeroDefect + ``` And so is: -.. code-block:: nim - + ```nim proc mydiv(a, b): int {.raises: [].} = if b == 0: raise newException(DivByZeroDefect, "division by zero") else: result = a div b + ``` The reason for this is that `DivByZeroDefect` inherits from `Defect` and @@ -4883,7 +5049,7 @@ EffectsOf annotation Rules 1-2 of the exception tracking inference rules (see the previous section) ensure the following works: -.. code-block:: nim + ```nim proc weDontRaiseButMaybeTheCallback(callback: proc()) {.raises: [], effectsOf: callback.} = callback() @@ -4893,6 +5059,7 @@ ensure the following works: proc use() {.raises: [].} = # doesn't compile! Can raise IOError! weDontRaiseButMaybeTheCallback(doRaise) + ``` As can be seen from the example, a parameter of type `proc (...)` can be annotated as `.effectsOf`. Such a parameter allows for effect polymorphism: @@ -4902,10 +5069,7 @@ that `callback` raises. So in many cases a callback does not cause the compiler to be overly conservative in its effect analysis: -.. code-block:: nim - :test: "nim c $1" - :status: 1 - + ```nim test = "nim c $1" status = 1 {.push warningAsError[Effect]: on.} {.experimental: "strictEffects".} @@ -4925,9 +5089,10 @@ conservative in its effect analysis: proc cmpE(a, b: MyInt): int {.raises: [Exception].} = cmp(a.int, b.int) - proc harmfull {.raises: [].} = + proc harmful {.raises: [].} = # does not compile, `sort` can now raise Exception toSort.sort cmpE + ``` @@ -4938,16 +5103,14 @@ Exception tracking is part of Nim's `effect system`:idx:. Raising an exception is an *effect*. Other effects can also be defined. A user defined effect is a means to *tag* a routine and to perform checks against this tag: -.. code-block:: nim - :test: "nim c --warningAsError:Effect:on $1" - :status: 1 - + ```nim test = "nim c --warningAsError:Effect:on $1" status = 1 type IO = object ## input/output effect proc readLine(): string {.tags: [IO].} = discard - proc no_IO_please() {.tags: [].} = + proc no_effects_please() {.tags: [].} = # the compiler prevents this: let x = readLine() + ``` A tag has to be a type name. A `tags` list - like a `raises` list - can also be attached to a proc type. This affects type compatibility. @@ -4955,6 +5118,51 @@ also be attached to a proc type. This affects type compatibility. The inference for tag tracking is analogous to the inference for exception tracking. +There is also a way which can be used to forbid certain effects: + + ```nim test = "nim c --warningAsError:Effect:on $1" status = 1 + type IO = object ## input/output effect + proc readLine(): string {.tags: [IO].} = discard + proc echoLine(): void = discard + + proc no_IO_please() {.forbids: [IO].} = + # this is OK because it didn't define any tag: + echoLine() + # the compiler prevents this: + let y = readLine() + ``` + +The `forbids` pragma defines a list of illegal effects - if any statement +invokes any of those effects, the compilation will fail. +Procedure types with any disallowed effect are the subtypes of equal +procedure types without such lists: + + ```nim + type MyEffect = object + type ProcType1 = proc (i: int): void {.forbids: [MyEffect].} + type ProcType2 = proc (i: int): void + + proc caller1(p: ProcType1): void = p(1) + proc caller2(p: ProcType2): void = p(1) + + proc effectful(i: int): void {.tags: [MyEffect].} = echo $i + proc effectless(i: int): void {.forbids: [MyEffect].} = echo $i + + proc toBeCalled1(i: int): void = effectful(i) + proc toBeCalled2(i: int): void = effectless(i) + + ## this will fail because toBeCalled1 uses MyEffect which was forbidden by ProcType1: + caller1(toBeCalled1) + ## this is OK because both toBeCalled2 and ProcType1 have the same requirements: + caller1(toBeCalled2) + ## these are OK because ProcType2 doesn't have any effect requirement: + caller2(toBeCalled1) + caller2(toBeCalled2) + ``` + +`ProcType2` is a subtype of `ProcType1`. Unlike with the `tags` pragma, the parent context - the +function which calls other functions with forbidden effects - doesn't inherit the forbidden list of effects. + Side effects ------------ @@ -4970,24 +5178,25 @@ or global variable and it does not call any routine that has a side effect. It is a static error to mark a proc/iterator to have no side effect if the compiler cannot verify this. -As a special semantic rule, the built-in `debugEcho -`_ pretends to be free of side effects +As a special semantic rule, the built-in [debugEcho]( +system.html#debugEcho,varargs[typed,]) pretends to be free of side effects so that it can be used for debugging routines marked as `noSideEffect`. `func` is syntactic sugar for a proc with no side effects: -.. code-block:: nim + ```nim func `+` (x, y: int): int + ``` To override the compiler's side effect analysis a `{.noSideEffect.}` `cast` pragma block can be used: -.. code-block:: nim - + ```nim func f() = {.cast(noSideEffect).}: echo "test" + ``` **Side effects are usually inferred. The inference for side effects is analogous to the inference for exception tracking.** @@ -5012,8 +5221,7 @@ Routines that are imported from C are always assumed to be `gcsafe`. To override the compiler's gcsafety analysis a `{.cast(gcsafe).}` pragma block can be used: -.. code-block:: nim - + ```nim var someGlobal: string = "some string here" perThread {.threadvar.}: string @@ -5021,11 +5229,12 @@ be used: proc setPerThread() = {.cast(gcsafe).}: deepCopy(perThread, someGlobal) + ``` See also: -- `Shared heap memory management `_. +- [Shared heap memory management](mm.html). @@ -5036,13 +5245,14 @@ The `effects` pragma has been designed to assist the programmer with the effects analysis. It is a statement that makes the compiler output all inferred effects up to the `effects`'s position: -.. code-block:: nim + ```nim proc p(what: bool) = if what: raise newException(IOError, "IO") {.effects.} else: raise newException(OSError, "OS") + ``` The compiler produces a hint message that `IOError` can be raised. `OSError` is not listed as it cannot be raised in the branch the `effects` pragma @@ -5058,12 +5268,10 @@ introduce type parameters or to instantiate a generic proc, iterator, or type. The following example shows how a generic binary tree can be modeled: -.. code-block:: nim - :test: "nim c $1" - + ```nim test = "nim c $1" type BinaryTree*[T] = ref object # BinaryTree is a generic type with - # generic param `T` + # generic parameter `T` le, ri: BinaryTree[T] # left and right subtrees; may be nil data: T # the data stored in a node @@ -5114,6 +5322,7 @@ The following example shows how a generic binary tree can be modeled: add(root, "world") # instantiates the second `add` proc for str in preorder(root): stdout.writeLine(str) + ``` The `T` is called a `generic type parameter`:idx: or a `type variable`:idx:. @@ -5125,13 +5334,14 @@ The `is` operator is evaluated during semantic analysis to check for type equivalence. It is therefore very useful for type specialization within generic code: -.. code-block:: nim + ```nim type Table[Key, Value] = object keys: seq[Key] values: seq[Value] when not (Key is string): # empty value for strings used for optimization deletedKeys: seq[bool] + ``` Type classes @@ -5165,20 +5375,22 @@ name that will match any instantiation of the generic type. Type classes can be combined using the standard boolean operators to form more complex type classes: -.. code-block:: nim + ```nim # create a type class that will match all tuple and object types type RecordType = tuple or object proc printFields[T: RecordType](rec: T) = for key, value in fieldPairs(rec): echo key, " = ", value + ``` Type constraints on generic parameters can be grouped with `,` and propagation stops with `;`, similarly to parameters for macros and templates: -.. code-block:: nim + ```nim proc fn1[T; U, V: SomeFloat]() = discard # T is unconstrained template fn2(t; u, v: SomeFloat) = discard # t is unconstrained + ``` Whilst the syntax of type classes appears to resemble that of ADTs/algebraic data types in ML-like languages, it should be understood that type classes are static @@ -5189,20 +5401,22 @@ runtime type dynamism, unlike object variants or methods. As an example, the following would not compile: -.. code-block:: nim + ```nim type TypeClass = int | string var foo: TypeClass = 2 # foo's type is resolved to an int here foo = "this will fail" # error here, because foo is an int + ``` Nim allows for type classes and regular types to be specified as `type constraints`:idx: of the generic type parameter: -.. code-block:: nim + ```nim proc onlyIntOrString[T: int|string](x, y: T) = discard onlyIntOrString(450, 616) # valid onlyIntOrString(5.0, 0.0) # type mismatch onlyIntOrString("xy", 50) # invalid as 'T' cannot be both at the same time + ``` Implicit generics @@ -5210,25 +5424,25 @@ Implicit generics A type class can be used directly as the parameter's type. -.. code-block:: nim - + ```nim # create a type class that will match all tuple and object types type RecordType = tuple or object proc printFields(rec: RecordType) = for key, value in fieldPairs(rec): echo key, " = ", value + ``` Procedures utilizing type classes in such a manner are considered to be `implicitly generic`:idx:. They will be instantiated once for each unique -combination of param types used within the program. +combination of parameter types used within the program. By default, during overload resolution, each named type class will bind to exactly one concrete type. We call such type classes `bind once`:idx: types. Here is an example taken directly from the system module to illustrate this: -.. code-block:: nim + ```nim proc `==`*(x, y: tuple): bool = ## requires `x` and `y` to be of the same tuple type ## generic `==` operator for tuples that is lifted from the components @@ -5236,81 +5450,82 @@ Here is an example taken directly from the system module to illustrate this: result = true for a, b in fields(x, y): if a != b: result = false + ``` Alternatively, the `distinct` type modifier can be applied to the type class -to allow each param matching the type class to bind to a different type. Such +to allow each parameter matching the type class to bind to a different type. Such type classes are called `bind many`:idx: types. Procs written with the implicitly generic style will often need to refer to the type parameters of the matched generic type. They can be easily accessed using the dot syntax: -.. code-block:: nim + ```nim type Matrix[T, Rows, Columns] = object ... proc `[]`(m: Matrix, row, col: int): Matrix.T = m.data[col * high(Matrix.Columns) + row] + ``` Here are more examples that illustrate implicit generics: -.. code-block:: nim - + ```nim proc p(t: Table; k: Table.Key): Table.Value # is roughly the same as: proc p[Key, Value](t: Table[Key, Value]; k: Key): Value + ``` -.. code-block:: nim - + ```nim proc p(a: Table, b: Table) # is roughly the same as: proc p[Key, Value](a, b: Table[Key, Value]) + ``` -.. code-block:: nim - + ```nim proc p(a: Table, b: distinct Table) # is roughly the same as: proc p[Key, Value, KeyB, ValueB](a: Table[Key, Value], b: Table[KeyB, ValueB]) + ``` `typedesc` used as a parameter type also introduces an implicit generic. `typedesc` has its own set of rules: -.. code-block:: nim - + ```nim proc p(a: typedesc) # is roughly the same as: proc p[T](a: typedesc[T]) + ``` `typedesc` is a "bind many" type class: -.. code-block:: nim - + ```nim proc p(a, b: typedesc) # is roughly the same as: proc p[T, T2](a: typedesc[T], b: typedesc[T2]) + ``` A parameter of type `typedesc` is itself usable as a type. If it is used -as a type, it's the underlying type. (In other words, one level +as a type, it's the underlying type. In other words, one level of "typedesc"-ness is stripped off: -.. code-block:: nim - + ```nim proc p(a: typedesc; b: a) = discard # is roughly the same as: @@ -5319,6 +5534,7 @@ of "typedesc"-ness is stripped off: # hence this is a valid call: p(int, 4) # as parameter 'a' requires a type, but 'b' requires a value. + ``` Generic inference restrictions @@ -5327,10 +5543,7 @@ Generic inference restrictions The types `var T` and `typedesc[T]` cannot be inferred in a generic instantiation. The following is not allowed: -.. code-block:: nim - :test: "nim c $1" - :status: 1 - + ```nim test = "nim c $1" status = 1 proc g[T](f: proc(x: T); x: T) = f(x) @@ -5347,14 +5560,14 @@ instantiation. The following is not allowed: # also not allowed: explicit instantiation via 'var int' g[var int](v, i) + ``` Symbol lookup in generics ------------------------- -Open and Closed symbols -~~~~~~~~~~~~~~~~~~~~~~~ +### Open and Closed symbols The symbol binding rules in generics are slightly subtle: There are "open" and "closed" symbols. A "closed" symbol cannot be re-bound in the instantiation @@ -5364,9 +5577,7 @@ and every other symbol is closed. Open symbols are looked up in two different contexts: Both the context at definition and the context at instantiation are considered: -.. code-block:: nim - :test: "nim c $1" - + ```nim test = "nim c $1" type Index = distinct int @@ -5376,6 +5587,7 @@ at definition and the context at instantiation are considered: var b = (0, 0.Index) echo a == b # works! + ``` In the example, the generic `==` for tuples (as defined in the system module) uses the `==` operators of the tuple's components. However, the `==` for @@ -5388,15 +5600,14 @@ Mixin statement A symbol can be forced to be open by a `mixin`:idx: declaration: -.. code-block:: nim - :test: "nim c $1" - + ```nim test = "nim c $1" proc create*[T](): ref T = # there is no overloaded 'init' here, so we need to state that it's an # open symbol explicitly: mixin init new result init result + ``` `mixin` statements only make sense in templates and generics. @@ -5409,7 +5620,7 @@ can be used to explicitly declare identifiers that should be bound early (i.e. the identifiers should be looked up in the scope of the template/generic definition): -.. code-block:: nim + ```nim # Module A var lastId = 0 @@ -5418,12 +5629,14 @@ definition): bind lastId inc(lastId) lastId + ``` -.. code-block:: nim + ```nim # Module B import A echo genId() + ``` But a `bind` is rarely useful because symbol binding from the definition scope is the default. @@ -5437,16 +5650,15 @@ Delegating bind statements The following example outlines a problem that can arise when generic instantiations cross multiple different modules: -.. code-block:: nim - + ```nim # module A proc genericA*[T](x: T) = mixin init init(x) + ``` -.. code-block:: nim - + ```nim import C # module B @@ -5455,19 +5667,20 @@ instantiations cross multiple different modules: # not available when `genericB` is instantiated: bind init genericA(x) + ``` -.. code-block:: nim - + ```nim # module C type O = object proc init*(x: var O) = discard + ``` -.. code-block:: nim - + ```nim # module main import B, C genericB O() + ``` In module B has an `init` proc from module C in its scope that is not taken into account when `genericB` is instantiated which leads to the @@ -5486,12 +5699,13 @@ The syntax to *invoke* a template is the same as calling a procedure. Example: -.. code-block:: nim + ```nim template `!=` (a, b: untyped): untyped = - # this definition exists in the System module + # this definition exists in the system module not (a == b) assert(5 != 6) # the compiler rewrites that to: assert(not (5 == 6)) + ``` The `!=`, `>`, `>=`, `in`, `notin`, `isnot` operators are in fact templates: @@ -5513,24 +5727,21 @@ An `untyped` parameter means that symbol lookups and type resolution is not performed before the expression is passed to the template. This means that *undeclared* identifiers, for example, can be passed to the template: -.. code-block:: nim - :test: "nim c $1" - + ```nim test = "nim c $1" template declareInt(x: untyped) = var x: int declareInt(x) # valid x = 3 + ``` -.. code-block:: nim - :test: "nim c $1" - :status: 1 - + ```nim test = "nim c $1" status = 1 template declareInt(x: typed) = var x: int declareInt(x) # invalid, because x has not been declared and so it has no type + ``` A template where every parameter is `untyped` is called an `immediate`:idx: template. For historical reasons, templates can be explicitly annotated with @@ -5548,9 +5759,7 @@ Passing a code block to a template One can pass a block of statements as the last argument to a template following the special `:` syntax: -.. code-block:: nim - :test: "nim c $1" - + ```nim test = "nim c $1" template withFile(f, fn, mode, actions: untyped): untyped = var f: File if open(f, fn, mode): @@ -5564,6 +5773,7 @@ following the special `:` syntax: withFile(txt, "ttempl3.txt", fmWrite): # special colon txt.writeLine("line 1") txt.writeLine("line 2") + ``` In the example, the two `writeLine` statements are bound to the `actions` parameter. @@ -5573,10 +5783,7 @@ Usually, to pass a block of code to a template, the parameter that accepts the block needs to be of type `untyped`. Because symbol lookups are then delayed until template instantiation time: -.. code-block:: nim - :test: "nim c $1" - :status: 1 - + ```nim test = "nim c $1" status = 1 template t(body: typed) = proc p = echo "hey" block: @@ -5584,6 +5791,7 @@ delayed until template instantiation time: t: p() # fails with 'undeclared identifier: p' + ``` The above code fails with the error message that `p` is not declared. The reason for this is that the `p()` body is type-checked before getting @@ -5591,9 +5799,7 @@ passed to the `body` parameter and type checking in Nim implies symbol lookups. The same code works with `untyped` as the passed body is not required to be type-checked: -.. code-block:: nim - :test: "nim c $1" - + ```nim test = "nim c $1" template t(body: untyped) = proc p = echo "hey" block: @@ -5601,6 +5807,7 @@ type-checked: t: p() # compiles + ``` Varargs of untyped @@ -5609,12 +5816,11 @@ Varargs of untyped In addition to the `untyped` meta-type that prevents type checking, there is also `varargs[untyped]` so that not even the number of parameters is fixed: -.. code-block:: nim - :test: "nim c $1" - + ```nim test = "nim c $1" template hideIdentifiers(x: varargs[untyped]) = discard hideIdentifiers(undeclared1, undeclared2) + ``` However, since a template cannot iterate over varargs, this feature is generally much more useful for macros. @@ -5626,7 +5832,7 @@ Symbol binding in templates A template is a `hygienic`:idx: macro and so opens a new scope. Most symbols are bound from the definition scope of the template: -.. code-block:: nim + ```nim # Module A var lastId = 0 @@ -5634,12 +5840,14 @@ bound from the definition scope of the template: template genId*: untyped = inc(lastId) lastId + ``` -.. code-block:: nim + ```nim # Module B import A echo genId() # Works as 'lastId' has been bound in 'genId's defining scope + ``` As in generics, symbol binding can be influenced via `mixin` or `bind` statements. @@ -5651,9 +5859,7 @@ Identifier construction In templates, identifiers can be constructed with the backticks notation: -.. code-block:: nim - :test: "nim c $1" - + ```nim test = "nim c $1" template typedef(name: untyped, typ: typedesc) = type `T name`* {.inject.} = typ @@ -5661,6 +5867,7 @@ In templates, identifiers can be constructed with the backticks notation: typedef(myint, int) var x: PMyInt + ``` In the example, `name` is instantiated with `myint`, so \`T name\` becomes `Tmyint`. @@ -5673,7 +5880,7 @@ A parameter `p` in a template is even substituted in the expression `x.p`. Thus, template arguments can be used as field names and a global symbol can be shadowed by the same argument name even when fully qualified: -.. code-block:: nim + ```nim # module 'm' type @@ -5687,10 +5894,11 @@ shadowed by the same argument name even when fully qualified: tstLev(levA) # produces: 'levA levA' + ``` But the global symbol can properly be captured by a `bind` statement: -.. code-block:: nim + ```nim # module 'm' type @@ -5705,6 +5913,7 @@ But the global symbol can properly be captured by a `bind` statement: tstLev(levA) # produces: 'levA levB' + ``` Hygiene in templates @@ -5713,9 +5922,7 @@ Hygiene in templates Per default, templates are `hygienic`:idx:\: Local identifiers declared in a template cannot be accessed in the instantiation context: -.. code-block:: nim - :test: "nim c $1" - + ```nim test = "nim c $1" template newException*(exceptn: typedesc, message: string): untyped = var e: ref exceptn # e is implicitly gensym'ed here @@ -5726,6 +5933,7 @@ template cannot be accessed in the instantiation context: # so this works: let e = "message" raise newException(IoError, e) + ``` Whether a symbol that is declared in a template is exposed to the instantiation @@ -5737,25 +5945,27 @@ is `gensym` and for `proc`, `iterator`, `converter`, `template`, `macro` is `inject`. However, if the name of the entity is passed as a template parameter, it is an `inject`'ed symbol: -.. code-block:: nim + ```nim template withFile(f, fn, mode: untyped, actions: untyped): untyped = block: - var f: File # since 'f' is a template param, it's injected implicitly + var f: File # since 'f' is a template parameter, it's injected implicitly ... withFile(txt, "ttempl3.txt", fmWrite): txt.writeLine("line 1") txt.writeLine("line 2") + ``` The `inject` and `gensym` pragmas are second class annotations; they have -no semantics outside of a template definition and cannot be abstracted over: +no semantics outside a template definition and cannot be abstracted over: -.. code-block:: nim + ```nim {.pragma myInject: inject.} template t() = var x {.myInject.}: int # does NOT work + ``` To get rid of hygiene in templates, one can use the `dirty`:idx: pragma for @@ -5767,9 +5977,7 @@ and `namedParameterCall(field = value)` syntactic constructs. The reason for this is that code like -.. code-block:: nim - :test: "nim c $1" - + ```nim test = "nim c $1" type T = object f: int @@ -5777,6 +5985,7 @@ The reason for this is that code like template tmp(x: T) = let f = 34 echo x.f, T(f: 4) + ``` should work as expected. @@ -5784,10 +5993,7 @@ should work as expected. However, this means that the method call syntax is not available for `gensym`'ed symbols: -.. code-block:: nim - :test: "nim c $1" - :status: 1 - + ```nim test = "nim c $1" status = 1 template tmp(x) = type T {.gensym.} = int @@ -5795,6 +6001,7 @@ However, this means that the method call syntax is not available for echo x.T # invalid: instead use: 'echo T(x)'. tmp(12) + ``` Limitations of the method call syntax @@ -5802,35 +6009,31 @@ Limitations of the method call syntax The expression `x` in `x.f` needs to be semantically checked (that means symbol lookup and type checking) before it can be decided that it needs to be -rewritten to `f(x)`. Therefore the dot syntax has some limitations when it +rewritten to `f(x)`. Therefore, the dot syntax has some limitations when it is used to invoke templates/macros: -.. code-block:: nim - :test: "nim c $1" - :status: 1 - + ```nim test = "nim c $1" status = 1 template declareVar(name: untyped) = const name {.inject.} = 45 # Doesn't compile: unknownIdentifier.declareVar + ``` It is also not possible to use fully qualified identifiers with module symbol in method call syntax. The order in which the dot operator binds to symbols prohibits this. -.. code-block:: nim - :test: "nim c $1" - :status: 1 - - import std/sequtils + ```nim test = "nim c $1" status = 1 + import std/sequtils - var myItems = @[1,3,3,7] - let N1 = count(myItems, 3) # OK - let N2 = sequtils.count(myItems, 3) # fully qualified, OK - let N3 = myItems.count(3) # OK - let N4 = myItems.sequtils.count(3) # illegal, `myItems.sequtils` can't be resolved + var myItems = @[1,3,3,7] + let N1 = count(myItems, 3) # OK + let N2 = sequtils.count(myItems, 3) # fully qualified, OK + let N3 = myItems.count(3) # OK + let N4 = myItems.sequtils.count(3) # illegal, `myItems.sequtils` can't be resolved + ``` This means that when for some reason a procedure needs a disambiguation through the module name, the call needs to be @@ -5873,9 +6076,7 @@ Debug example The following example implements a powerful `debug` command that accepts a variable number of arguments: -.. code-block:: nim - :test: "nim c $1" - + ```nim test = "nim c $1" # to work with Nim syntax trees, we need an API that is defined in the # `macros` module: import std/macros @@ -5903,10 +6104,11 @@ variable number of arguments: a[1] = 45 debug(a[0], a[1], x) + ``` The macro call expands to: -.. code-block:: nim + ```nim write(stdout, "a[0]") write(stdout, ": ") writeLine(stdout, a[0]) @@ -5918,6 +6120,7 @@ The macro call expands to: write(stdout, "x") write(stdout, ": ") writeLine(stdout, x) + ``` Arguments that are passed to a `varargs` parameter are wrapped in an array @@ -5934,9 +6137,7 @@ instantiating context. There is a way to use bound identifiers (aka `symbols`:idx:) instead of using unbound identifiers. The `bindSym` builtin can be used for that: -.. code-block:: nim - :test: "nim c $1" - + ```nim test = "nim c $1" import std/macros macro debug(n: varargs[typed]): untyped = @@ -5954,10 +6155,11 @@ builtin can be used for that: a[1] = 45 debug(a[0], a[1], x) + ``` The macro call expands to: -.. code-block:: nim + ```nim write(stdout, "a[0]") write(stdout, ": ") writeLine(stdout, a[0]) @@ -5969,14 +6171,15 @@ The macro call expands to: write(stdout, "x") write(stdout, ": ") writeLine(stdout, x) + ``` -However, the symbols `write`, `writeLine` and `stdout` are already bound -and are not looked up again. As the example shows, `bindSym` does work with -overloaded symbols implicitly. +In this version of `debug`, the symbols `write`, `writeLine` and `stdout` +are already bound and are not looked up again. As the example shows, `bindSym` +does work with overloaded symbols implicitly. Note that the symbol names passed to `bindSym` have to be constant. The -experimental feature `dynamicBindSym` (`experimental manual -`_) +experimental feature `dynamicBindSym` ([experimental manual]( +manual_experimental.html#dynamic-arguments-for-bindsym)) allows this value to be computed dynamically. Post-statement blocks @@ -5986,7 +6189,7 @@ Macros can receive `of`, `elif`, `else`, `except`, `finally` and `do` blocks (including their different forms such as `do` with routine parameters) as arguments if called in statement form. -.. code-block:: nim + ```nim macro performWithUndo(task, undo: untyped) = ... performWithUndo do: @@ -5994,7 +6197,7 @@ as arguments if called in statement form. # to perform the task do: # code to undo it - + let num = 12 # a single colon may be used if there is no initial block match (num mod 3, num mod 5): @@ -6006,6 +6209,7 @@ as arguments if called in statement form. echo "Buzz" else: echo num + ``` For loop macro @@ -6014,9 +6218,7 @@ For loop macro A macro that takes as its only input parameter an expression of the special type `system.ForLoopStmt` can rewrite the entirety of a `for` loop: -.. code-block:: nim - :test: "nim c $1" - + ```nim test = "nim c $1" import std/macros macro example(loop: ForLoopStmt) = @@ -6026,18 +6228,18 @@ type `system.ForLoopStmt` can rewrite the entirety of a `for` loop: result.add newCall(bindSym"echo", loop[0]) for item in example([1, 2, 3]): discard + ``` Expands to: -.. code-block:: nim + ```nim for item in items([1, 2, 3]): echo item + ``` Another example: -.. code-block:: nim - :test: "nim c $1" - + ```nim test = "nim c $1" import std/macros macro enumerate(x: ForLoopStmt): untyped = @@ -6069,6 +6271,7 @@ Another example: # names for `a` and `b` here to avoid redefinition errors for a, b in enumerate(10, [1, 2, 3, 5]): echo a, " ", b + ``` Case statement macros @@ -6079,8 +6282,7 @@ for certain types. The following is an example of such an implementation for tuples, leveraging the existing equality operator for tuples (as provided in `system.==`): -.. code-block:: nim - :test: "nim c $1" + ```nim test = "nim c $1" import std/macros macro `case`(n: tuple): untyped = @@ -6102,6 +6304,7 @@ for tuples, leveraging the existing equality operator for tuples of ("foo", 78): echo "yes" of ("bar", 88): echo "no" else: discard + ``` `case` macros are subject to overload resolution. The type of the `case` statement's selector expression is matched against the type @@ -6116,13 +6319,12 @@ macro to call. Special Types ============= -static[T] ---------- +static\[T] +---------- As their name suggests, static parameters must be constant expressions: -.. code-block:: nim - + ```nim proc precompiledRegex(pattern: static string): RegEx = var res {.global.} = re(pattern) return res @@ -6132,16 +6334,16 @@ As their name suggests, static parameters must be constant expressions: precompiledRegex(paramStr(1)) # Error, command-line options # are not constant expressions + ``` -For the purposes of code generation, all static params are treated as -generic params - the proc will be compiled separately for each unique +For the purposes of code generation, all static parameters are treated as +generic parameters - the proc will be compiled separately for each unique supplied value (or combination of values). -Static params can also appear in the signatures of generic types: - -.. code-block:: nim +Static parameters can also appear in the signatures of generic types: + ```nim type Matrix[M,N: static int; T: Number] = array[0..(M*N - 1), T] # Note how `Number` is just a type constraint here, while @@ -6152,25 +6354,27 @@ Static params can also appear in the signatures of generic types: var m1: AffineTransform3D[float] # OK var m2: AffineTransform2D[string] # Error, `string` is not a `Number` + ``` Please note that `static T` is just a syntactic convenience for the underlying -generic type `static[T]`. The type param can be omitted to obtain the type +generic type `static[T]`. The type parameter can be omitted to obtain the type class of all constant expressions. A more specific type class can be created by instantiating `static` with another type class. One can force an expression to be evaluated at compile time as a constant expression by coercing it to a corresponding `static` type: -.. code-block:: nim + ```nim import std/math echo static(fac(5)), " ", static[bool](16.isPowerOfTwo) + ``` The compiler will report any failure to evaluate the expression or a possible type mismatch error. -typedesc[T] ------------ +typedesc\[T] +------------ In many contexts, Nim treats the names of types as regular values. These values exist only during the compilation phase, but since @@ -6178,48 +6382,46 @@ all values must have a type, `typedesc` is considered their special type. `typedesc` acts as a generic type. For instance, the type of the symbol `int` is `typedesc[int]`. Just like with regular generic types, when the -generic param is omitted, `typedesc` denotes the type class of all types. +generic parameter is omitted, `typedesc` denotes the type class of all types. As a syntactic convenience, one can also use `typedesc` as a modifier. -Procs featuring `typedesc` params are considered implicitly generic. +Procs featuring `typedesc` parameters are considered implicitly generic. They will be instantiated for each unique combination of supplied types, -and within the body of the proc, the name of each param will refer to +and within the body of the proc, the name of each parameter will refer to the bound concrete type: -.. code-block:: nim - + ```nim proc new(T: typedesc): ref T = echo "allocating ", T.name new(result) var n = Node.new var tree = new(BinaryTree[int]) + ``` -When multiple type params are present, they will bind freely to different -types. To force a bind-once behavior, one can use an explicit generic param: +When multiple type parameters are present, they will bind freely to different +types. To force a bind-once behavior, one can use an explicit generic parameter: -.. code-block:: nim + ```nim proc acceptOnlyTypePairs[T, U](A, B: typedesc[T]; C, D: typedesc[U]) + ``` -Once bound, type params can appear in the rest of the proc signature: - -.. code-block:: nim - :test: "nim c $1" +Once bound, type parameters can appear in the rest of the proc signature: + ```nim test = "nim c $1" template declareVariableWithType(T: typedesc, value: T) = var x: T = value declareVariableWithType int, 42 + ``` Overload resolution can be further influenced by constraining the set -of types that will match the type param. This works in practice by +of types that will match the type parameter. This works in practice by attaching attributes to types via templates. The constraint can be a concrete type or a type class. -.. code-block:: nim - :test: "nim c $1" - + ```nim test = "nim c $1" template maxval(T: typedesc[int]): int = high(int) template maxval(T: typedesc[float]): float = Inf @@ -6235,13 +6437,13 @@ concrete type or a type class. echo "is int a number? ", isNumber(int) echo "is float a number? ", isNumber(float) echo "is RootObj a number? ", isNumber(RootObj) + ``` Passing `typedesc` is almost identical, just with the difference that the macro is not instantiated generically. The type expression is simply passed as a `NimNode` to the macro, like everything else. -.. code-block:: nim - + ```nim import std/macros macro forwardType(arg: typedesc): typedesc = @@ -6250,6 +6452,7 @@ simply passed as a `NimNode` to the macro, like everything else. result = tmp var tmp: forwardType(int) + ``` typeof operator --------------- @@ -6261,10 +6464,10 @@ One can obtain the type of a given expression by constructing a `typeof` value from it (in many other languages this is known as the `typeof`:idx: operator): -.. code-block:: nim - + ```nim var x = 0 var y: typeof(x) # y has type int + ``` If `typeof` is used to determine the result type of a proc/iterator/converter @@ -6273,16 +6476,15 @@ interpretation, where `c` is an iterator, is preferred over the other interpretations, but this behavior can be changed by passing `typeOfProc` as the second argument to `typeof`: -.. code-block:: nim - :test: "nim c $1" - + ```nim test = "nim c $1" iterator split(s: string): string = discard proc split(s: string): seq[string] = discard - # since an iterator is the preferred interpretation, `y` has the type `string`: + # since an iterator is the preferred interpretation, this has the type `string`: assert typeof("a b c".split) is string assert typeof("a b c".split, typeOfProc) is seq[string] + ``` @@ -6291,7 +6493,7 @@ Modules Nim supports splitting a program into pieces by a module concept. Each module needs to be in its own file and has its own `namespace`:idx:. Modules enable `information hiding`:idx: and `separate compilation`:idx:. -A module may gain access to symbols of another module by the `import`:idx: +A module may gain access to the symbols of another module by the `import`:idx: statement. `Recursive module dependencies`:idx: are allowed, but are slightly subtle. Only top-level symbols that are marked with an asterisk (`*`) are exported. A valid module name can only be a valid Nim identifier (and thus its @@ -6306,7 +6508,7 @@ The algorithm for compiling modules is: This is best illustrated by an example: -.. code-block:: nim + ```nim # Module A type T1* = int # Module A exports the type `T1` @@ -6316,9 +6518,10 @@ This is best illustrated by an example: var i = p(3) # works because B has been parsed completely here main() + ``` -.. code-block:: nim + ```nim # Module B import A # A is not parsed here! Only the already known symbols # of A are imported. @@ -6327,28 +6530,27 @@ This is best illustrated by an example: # this works because the compiler has already # added T1 to A's interface symbol table result = x + 1 + ``` Import statement ---------------- -After the `import` statement, a list of module names can follow or a single +After the `import` keyword, a list of module names can follow or a single module name followed by an `except` list to prevent some symbols from being imported: -.. code-block:: nim - :test: "nim c $1" - :status: 1 - + ```nim test = "nim c $1" status = 1 import std/strutils except `%`, toUpperAscii # doesn't work then: echo "$1" % "abc".toUpperAscii + ``` It is not checked that the `except` list is really exported from the module. -This feature allows us to compile against an older version of the module that -does not export these identifiers. +This feature allows us to compile against different versions of the module, +even when one version does not export some of these identifiers. The `import` statement is only allowed at the top level. @@ -6360,51 +6562,58 @@ The `include` statement does something fundamentally different than importing a module: it merely includes the contents of a file. The `include` statement is useful to split up a large module into several files: -.. code-block:: nim + ```nim include fileA, fileB, fileC + ``` -The `include` statement can be used outside of the top level, as such: +The `include` statement can be used outside the top level, as such: -.. code-block:: nim + ```nim # Module A echo "Hello World!" + ``` -.. code-block:: nim + ```nim # Module B proc main() = include A main() # => Hello World! + ``` Module names in imports ----------------------- -A module alias can be introduced via the `as` keyword: +A module alias can be introduced via the `as` keyword, after which the original module name +is inaccessible: -.. code-block:: nim + ```nim import std/strutils as su, std/sequtils as qu echo su.format("$1", "lalelu") + ``` -The original module name is then not accessible. The notations -`path/to/module` or `"path/to/module"` can be used to refer to a module +The notations `path/to/module` or `"path/to/module"` can be used to refer to a module in subdirectories: -.. code-block:: nim + ```nim import lib/pure/os, "lib/pure/times" + ``` -Note that the module name is still `strutils` and not `lib/pure/strutils` -and so one **cannot** do: +Note that the module name is still `strutils` and not `lib/pure/strutils`, +thus one **cannot** do: -.. code-block:: nim + ```nim import lib/pure/strutils echo lib/pure/strutils.toUpperAscii("abc") + ``` Likewise, the following does not make sense as the name is `strutils` already: -.. code-block:: nim + ```nim import lib/pure/strutils as strutils + ``` Collective imports from a directory @@ -6416,8 +6625,9 @@ from the same directory. Path names are syntactically either Nim identifiers or string literals. If the path name is not a valid Nim identifier it needs to be a string literal: -.. code-block:: nim + ```nim import "gfx/3d/somemodule" # in quotes because '3d' is not a valid Nim identifier + ``` Pseudo import/include paths @@ -6441,18 +6651,17 @@ It is recommended and preferred but not currently enforced that all stdlib modul From import statement --------------------- -After the `from` statement, a module name follows followed by +After the `from` keyword, a module name followed by an `import` to list the symbols one likes to use without explicit full qualification: -.. code-block:: nim - :test: "nim c $1" - + ```nim test = "nim c $1" from std/strutils import `%` echo "$1" % "abc" # always possible: full qualification: echo strutils.replace("abc", "a", "z") + ``` It's also possible to use `from module import nil` if one wants to import the module but wants to enforce fully qualified access to every symbol @@ -6465,34 +6674,38 @@ Export statement An `export` statement can be used for symbol forwarding so that client modules don't need to import a module's dependencies: -.. code-block:: nim + ```nim # module B type MyObject* = object + ``` -.. code-block:: nim + ```nim # module A import B export B.MyObject proc `$`*(x: MyObject): string = "my object" + ``` -.. code-block:: nim + ```nim # module C import A # B.MyObject has been imported implicitly here: var x: MyObject echo $x + ``` When the exported symbol is another module, all of its definitions will be forwarded. One can use an `except` list to exclude some of the symbols. Notice that when exporting, one needs to specify only the module name: -.. code-block:: nim + ```nim import foo/bar/baz export baz + ``` @@ -6503,8 +6716,8 @@ the block in which the declaration occurred. The range where the identifier is known is the scope of the identifier. The exact scope of an identifier depends on the way it was declared. -Block scope -~~~~~~~~~~~ +### Block scope + The *scope* of a variable declared in the declaration part of a block is valid from the point of declaration until the end of the block. If a block contains a second block, in which the identifier is redeclared, @@ -6514,8 +6727,8 @@ identifier cannot be redefined in the same block, except if valid for procedure or iterator overloading purposes. -Tuple or object scope -~~~~~~~~~~~~~~~~~~~~~ +### Tuple or object scope + The field identifiers inside a tuple or object definition are valid in the following places: @@ -6523,8 +6736,8 @@ following places: * Field designators of a variable of the given tuple/object type. * In all descendant types of the object type. -Module scope -~~~~~~~~~~~~ +### Module scope + All identifiers of a module are valid from the point of declaration until the end of the module. Identifiers from indirectly dependent modules are *not* available. The `system`:idx: module is automatically imported in every module. @@ -6533,15 +6746,17 @@ If a module imports an identifier by two different modules, each occurrence of the identifier has to be qualified unless it is an overloaded procedure or iterator in which case the overloading resolution takes place: -.. code-block:: nim + ```nim # Module A var x*: string + ``` -.. code-block:: nim + ```nim # Module B var x*: int + ``` -.. code-block:: nim + ```nim # Module C import A, B write(stdout, x) # error: x is ambiguous @@ -6549,6 +6764,20 @@ iterator in which case the overloading resolution takes place: var x = 4 write(stdout, x) # not ambiguous: uses the module C's x + ``` + + +Packages +-------- +A collection of modules in a file tree with an ``identifier.nimble`` file in the +root of the tree is called a Nimble package. A valid package name can only be a +valid Nim identifier and thus its filename is ``identifier.nimble`` where +``identifier`` is the desired package name. A module without a ``.nimble`` file +is assigned the package identifier: `unknown`. + +The distinction between packages allows diagnostic compiler messages to be +scoped to the current project's package vs foreign packages. + Compiler Messages @@ -6576,14 +6805,16 @@ deprecated pragma The deprecated pragma is used to mark a symbol as deprecated: -.. code-block:: nim + ```nim proc p() {.deprecated.} var x {.deprecated.}: char + ``` This pragma can also take in an optional warning string to relay to developers. -.. code-block:: nim + ```nim proc thing(x: bool) {.deprecated: "use thong instead".} + ``` @@ -6595,23 +6826,23 @@ procs are useful as helpers for macros. Since version 0.12.0 of the language, a proc that uses `system.NimNode` within its parameter types is implicitly declared `compileTime`: -.. code-block:: nim + ```nim proc astHelper(n: NimNode): NimNode = result = n + ``` Is the same as: -.. code-block:: nim + ```nim proc astHelper(n: NimNode): NimNode {.compileTime.} = result = n + ``` `compileTime` variables are available at runtime too. This simplifies certain idioms where variables are filled at compile-time (for example, lookup tables) but accessed at runtime: -.. code-block:: nim - :test: "nim c -r $1" - + ```nim test = "nim c -r $1" import std/macros var nameToProc {.compileTime.}: seq[(string, proc (): string {.nimcall.})] @@ -6629,6 +6860,7 @@ but accessed at runtime: proc baz: string {.registerProc.} = "baz" doAssert nameToProc[2][1]() == "baz" + ``` noreturn pragma @@ -6642,20 +6874,22 @@ The `acyclic` pragma can be used for object types to mark them as acyclic even though they seem to be cyclic. This is an **optimization** for the garbage collector to not consider objects of this type as part of a cycle: -.. code-block:: nim + ```nim type Node = ref NodeObj NodeObj {.acyclic.} = object left, right: Node data: string + ``` Or if we directly use a ref object: -.. code-block:: nim + ```nim type Node {.acyclic.} = ref object left, right: Node data: string + ``` In the example, a tree structure is declared with the `Node` type. Note that the type definition is recursive and the GC has to assume that objects of @@ -6683,7 +6917,7 @@ because the semantics of Nim require deep copying of sequences and strings. This can be expensive, especially if sequences are used to build a tree structure: -.. code-block:: nim + ```nim type NodeKind = enum nkLeaf, nkInner Node {.shallow.} = object @@ -6692,6 +6926,7 @@ structure: strVal: string of nkInner: children: seq[Node] + ``` pure pragma @@ -6726,9 +6961,10 @@ annotate a symbol (like an iterator or proc). The *usage* of the symbol then triggers a static error. This is especially useful to rule out that some operation is valid due to overloading and type conversions: -.. code-block:: nim + ```nim ## check that underlying int values are compared and not the pointers: proc `==`(x, y: ptr int): bool {.error.} + ``` fatal pragma @@ -6737,9 +6973,10 @@ The `fatal` pragma is used to make the compiler output an error message with the given content. In contrast to the `error` pragma, the compilation is guaranteed to be aborted by this pragma. Example: -.. code-block:: nim + ```nim when not defined(objc): {.fatal: "Compile this program with the objc command!".} + ``` warning pragma -------------- @@ -6756,15 +6993,15 @@ line pragma The `line` pragma can be used to affect line information of the annotated statement, as seen in stack backtraces: -.. code-block:: nim - + ```nim template myassert*(cond: untyped, msg = "") = if not cond: # change run-time line information of the 'raise' statement: {.line: instantiationInfo().}: raise newException(AssertionDefect, msg) + ``` -If the `line` pragma is used with a parameter, the parameter needs be a +If the `line` pragma is used with a parameter, the parameter needs to be a `tuple[filename: string, line: int]`. If it is used without a parameter, `system.instantiationInfo()` is used. @@ -6775,7 +7012,7 @@ The `linearScanEnd` pragma can be used to tell the compiler how to compile a Nim `case`:idx: statement. Syntactically it has to be used as a statement: -.. code-block:: nim + ```nim case myInt of 0: echo "most common case" @@ -6784,9 +7021,10 @@ statement: echo "second most common case" of 2: echo "unlikely: use branch table" else: echo "unlikely too: use branch table for ", myInt + ``` In the example, the case branches `0` and `1` are much more common than -the other cases. Therefore the generated assembler code should test for these +the other cases. Therefore, the generated assembler code should test for these values first so that the CPU's branch predictor has a good chance to succeed (avoiding an expensive CPU pipeline stall). The other cases might be put into a jump table for O(1) overhead but at the cost of a (very likely) pipeline @@ -6803,8 +7041,7 @@ The `computedGoto` pragma can be used to tell the compiler how to compile a Nim `case`:idx: in a `while true` statement. Syntactically it has to be used as a statement inside the loop: -.. code-block:: nim - + ```nim type MyEnum = enum enumA, enumB, enumC, enumD, enumE @@ -6836,6 +7073,7 @@ Syntactically it has to be used as a statement inside the loop: inc(pc) vm() + ``` As the example shows, `computedGoto` is mostly useful for interpreters. If the underlying backend (C compiler) does not support the computed goto @@ -6845,9 +7083,24 @@ extension the pragma is simply ignored. immediate pragma ---------------- -The immediate pragma is obsolete. See `Typed vs untyped parameters -<#templates-typed-vs-untyped-parameters>`_. +The immediate pragma is obsolete. See [Typed vs untyped parameters]. + +redefine pragma +--------------- + +Redefinition of template symbols with the same signature is allowed. +This can be made explicit with the `redefine` pragma: + +```nim +template foo: int = 1 +echo foo() # 1 +template foo: int {.redefine.} = 2 +echo foo() # 2 +# warning: implicit redefinition of template +template foo: int = 3 +``` +This is mostly intended for macro generated code. compilation option pragmas -------------------------- @@ -6885,9 +7138,10 @@ callconv cdecl|... Specifies the default calling convention for Example: -.. code-block:: nim + ```nim {.checks: off, optimization: speed.} # compile without runtime checks and optimize for speed + ``` push and pop pragmas @@ -6895,16 +7149,17 @@ push and pop pragmas The `push/pop`:idx: pragmas are very similar to the option directive, but are used to override the settings temporarily. Example: -.. code-block:: nim + ```nim {.push checks: off.} # compile this section without runtime checks as it is # speed critical # ... some code ... {.pop.} # restore old settings + ``` `push/pop`:idx: can switch on/off some standard library pragmas, example: -.. code-block:: nim + ```nim {.push inline.} proc thisIsInlined(): int = 42 func willBeInlined(): float = 42.0 @@ -6918,6 +7173,7 @@ but are used to override the settings temporarily. Example: {.push deprecated, hint[LineTooLong]: off, used, stackTrace: off.} proc sample(): bool = true {.pop.} + ``` For third party pragmas, it depends on its implementation but uses the same syntax. @@ -6939,10 +7195,11 @@ The `global` pragma can be applied to a variable within a proc to instruct the compiler to store it in a global location and initialize it once at program startup. -.. code-block:: nim + ```nim proc isHexNumber(s: string): bool = var pattern {.global.} = re"[0-9a-fA-F]+" result = s.match(pattern) + ``` When used within a generic proc, a separate unique global variable will be created for each instantiation of the proc. The order of initialization of @@ -6954,11 +7211,13 @@ Disabling certain messages -------------------------- Nim generates some warnings and hints ("line too long") that may annoy the user. A mechanism for disabling certain messages is provided: Each hint -and warning message contains a symbol in brackets. This is the message's -identifier that can be used to enable or disable it: +and warning message is associated with a symbol. This is the message's +identifier, which can be used to enable or disable the message by putting it +in brackets following the pragma: -.. code-block:: Nim + ```Nim {.hint[LineTooLong]: off.} # turn off the hint about too long lines + ``` This is often better than disabling all warnings at once. @@ -6970,7 +7229,7 @@ Nim produces a warning for symbols that are not exported and not used either. The `used` pragma can be attached to a symbol to suppress this warning. This is particularly useful when the symbol was generated by a macro: -.. code-block:: nim + ```nim template implementArithOps(T) = proc echoAdd(a, b: T) {.used.} = echo a + b @@ -6980,18 +7239,19 @@ is particularly useful when the symbol was generated by a macro: # no warning produced for the unused 'echoSub' implementArithOps(int) echoAdd 3, 5 + ``` `used` can also be used as a top-level statement to mark a module as "used". This prevents the "Unused import" warning: -.. code-block:: nim - + ```nim # module: debughelper.nim when defined(nimHasUsed): # 'import debughelper' is so useful for debugging # that Nim shouldn't produce a warning for that import, # even if currently unused: {.used.} + ``` experimental pragma @@ -7001,11 +7261,11 @@ The `experimental` pragma enables experimental language features. Depending on the concrete feature, this means that the feature is either considered too unstable for an otherwise stable release or that the future of the feature is uncertain (it may be removed at any time). See the -`experimental manual `_ for more details. +[experimental manual](manual_experimental.html) for more details. Example: -.. code-block:: nim + ```nim import std/threadpool {.experimental: "parallel".} @@ -7018,6 +7278,7 @@ Example: spawn threadedEcho("echo in parallel", i) useParallel() + ``` As a top-level statement, the experimental pragma enables a feature for the @@ -7025,8 +7286,7 @@ rest of the module it's enabled in. This is problematic for macro and generic instantiations that cross a module scope. Currently, these usages have to be put into a `.push/pop` environment: -.. code-block:: nim - + ```nim # client.nim proc useParallel*[T](unused: T) = # use a generic T here to show the problem. @@ -7036,12 +7296,13 @@ put into a `.push/pop` environment: echo "echo in parallel" {.pop.} + ``` -.. code-block:: nim - + ```nim import client useParallel(1) + ``` Implementation Specific Pragmas @@ -7056,17 +7317,19 @@ Bitsize pragma The `bitsize` pragma is for object field members. It declares the field as a bitfield in C/C++. -.. code-block:: Nim + ```Nim type mybitfield = object flag {.bitsize:1.}: cuint + ``` generates: -.. code-block:: C + ```C struct mybitfield { unsigned int flag:1; }; + ``` Align pragma @@ -7079,31 +7342,31 @@ alignments that are weaker than other align pragmas on the same declaration are ignored. Alignments that are weaker than the alignment requirement of the type are ignored. -.. code-block:: Nim - - type - sseType = object - sseData {.align(16).}: array[4, float32] + ```Nim + type + sseType = object + sseData {.align(16).}: array[4, float32] - # every object will be aligned to 128-byte boundary - Data = object - x: char - cacheline {.align(128).}: array[128, char] # over-aligned array of char, + # every object will be aligned to 128-byte boundary + Data = object + x: char + cacheline {.align(128).}: array[128, char] # over-aligned array of char, - proc main() = - echo "sizeof(Data) = ", sizeof(Data), " (1 byte + 127 bytes padding + 128-byte array)" - # output: sizeof(Data) = 256 (1 byte + 127 bytes padding + 128-byte array) - echo "alignment of sseType is ", alignof(sseType) - # output: alignment of sseType is 16 - var d {.align(2048).}: Data # this instance of data is aligned even stricter + proc main() = + echo "sizeof(Data) = ", sizeof(Data), " (1 byte + 127 bytes padding + 128-byte array)" + # output: sizeof(Data) = 256 (1 byte + 127 bytes padding + 128-byte array) + echo "alignment of sseType is ", alignof(sseType) + # output: alignment of sseType is 16 + var d {.align(2048).}: Data # this instance of data is aligned even stricter - main() + main() + ``` This pragma has no effect on the JS backend. Noalias pragma -============== +-------------- Since version 1.4 of the Nim compiler, there is a `.noalias` annotation for variables and parameters. It is mapped directly to C/C++'s `restrict`:c: keyword and means that @@ -7113,14 +7376,14 @@ restriction is violated, the backend optimizer is free to miscompile the code. This is an **unsafe** language feature. Ideally in later versions of the language, the restriction will be enforced at -compile time. (This is also why the name `noalias` was choosen instead of a more +compile time. (This is also why the name `noalias` was chosen instead of a more verbose name like `unsafeAssumeNoAlias`.) Volatile pragma --------------- The `volatile` pragma is for variables only. It declares the variable as -`volatile`:c:, whatever that means in C/C++ (its semantics are not well defined +`volatile`:c:, whatever that means in C/C++ (its semantics are not well-defined in C/C++). **Note**: This pragma will not exist for the LLVM backend. @@ -7133,10 +7396,11 @@ type, etc.) and is sometimes useful for interoperability with C: It tells Nim that it should not generate a declaration for the symbol in the C code. For example: -.. code-block:: Nim + ```Nim var EACCES {.importc, nodecl.}: cint # pretend EACCES was a variable, as # Nim does not know its value + ``` However, the `header` pragma is often the better alternative. @@ -7149,10 +7413,11 @@ The `header` pragma is very similar to the `nodecl` pragma: It can be applied to almost any symbol and specifies that it should not be declared and instead, the generated code should contain an `#include`:c:\: -.. code-block:: Nim + ```Nim type PFile {.importc: "FILE*", header: "".} = distinct pointer # import C's FILE* type; Nim will treat it as a new pointer type + ``` The `header` pragma always expects a string constant. The string constant contains the header file: As usual for C, a system header file is enclosed @@ -7167,10 +7432,11 @@ IncompleteStruct pragma The `incompleteStruct` pragma tells the compiler to not use the underlying C `struct`:c: in a `sizeof` expression: -.. code-block:: Nim + ```Nim type DIR* {.importc: "DIR", header: "", pure, incompleteStruct.} = object + ``` Compile pragma @@ -7178,8 +7444,9 @@ Compile pragma The `compile` pragma can be used to compile and link a C/C++ source file with the project: -.. code-block:: Nim + ```Nim {.compile: "myfile.cpp".} + ``` **Note**: Nim computes a SHA1 checksum and only recompiles the file if it has changed. One can use the `-f`:option: command-line option to force @@ -7187,8 +7454,9 @@ the recompilation of the file. Since 1.4 the `compile` pragma is also available with this syntax: -.. code-block:: Nim + ```Nim {.compile("myfile.cpp", "--custom flags here").} + ``` As can be seen in the example, this new variant allows for custom flags that are passed to the C compiler when the file is recompiled. @@ -7198,24 +7466,27 @@ Link pragma ----------- The `link` pragma can be used to link an additional file with the project: -.. code-block:: Nim + ```Nim {.link: "myfile.o".} + ``` -PassC pragma +passc pragma ------------ The `passc` pragma can be used to pass additional parameters to the C -compiler like one would using the command-line switch `--passc`:option:\: +compiler like one would use the command-line switch `--passc`:option:\: -.. code-block:: Nim + ```Nim {.passc: "-Wall -Werror".} + ``` -Note that one can use `gorge` from the `system module `_ to +Note that one can use `gorge` from the [system module](system.html) to embed parameters from an external command that will be executed during semantic analysis: -.. code-block:: Nim + ```Nim {.passc: gorge("pkg-config --cflags sdl").} + ``` localPassC pragma @@ -7224,26 +7495,29 @@ The `localPassC` pragma can be used to pass additional parameters to the C compiler, but only for the C/C++ file that is produced from the Nim module the pragma resides in: -.. code-block:: Nim + ```Nim # Module A.nim # Produces: A.nim.cpp {.localPassC: "-Wall -Werror".} # Passed when compiling A.nim.cpp + ``` -PassL pragma +passl pragma ------------ -The `passL` pragma can be used to pass additional parameters to the linker -like one would be using the command-line switch `--passL`:option:\: +The `passl` pragma can be used to pass additional parameters to the linker +like one would be using the command-line switch `--passl`:option:\: -.. code-block:: Nim - {.passL: "-lSDLmain -lSDL".} + ```Nim + {.passl: "-lSDLmain -lSDL".} + ``` -Note that one can use `gorge` from the `system module `_ to +Note that one can use `gorge` from the [system module](system.html) to embed parameters from an external command that will be executed during semantic analysis: -.. code-block:: Nim - {.passL: gorge("pkg-config --libs sdl").} + ```Nim + {.passl: gorge("pkg-config --libs sdl").} + ``` Emit pragma @@ -7255,7 +7529,7 @@ extremely useful for interfacing with `C++`:idx: or `Objective C`:idx: code. Example: -.. code-block:: Nim + ```Nim {.emit: """ static int cvariable = 420; """.} @@ -7268,17 +7542,19 @@ Example: {.pop.} embedsC() + ``` ``nimbase.h`` defines `NIM_EXTERNC`:c: C macro that can be used for `extern "C"`:cpp: code to work with both `nim c`:cmd: and `nim cpp`:cmd:, e.g.: -.. code-block:: Nim + ```Nim proc foobar() {.importc:"$1".} {.emit: """ #include NIM_EXTERNC void fun(){} """.} + ``` .. note:: For backward compatibility, if the argument to the `emit` statement is a single string literal, Nim symbols can be referred to via backticks. @@ -7288,7 +7564,7 @@ For a top-level emit statement, the section where in the generated C/C++ file the code should be emitted can be influenced via the prefixes `/*TYPESECTION*/`:c: or `/*VARSECTION*/`:c: or `/*INCLUDESECTION*/`:c:\: -.. code-block:: Nim + ```Nim {.emit: """/*TYPESECTION*/ struct Vector3 { public: @@ -7302,24 +7578,25 @@ the code should be emitted can be influenced via the prefixes x: cfloat proc constructVector3(a: cfloat): Vector3 {.importcpp: "Vector3(@)", nodecl} + ``` ImportCpp pragma ---------------- -**Note**: `c2nim `_ can parse a large subset of C++ and knows +**Note**: [c2nim](https://github.com/nim-lang/c2nim/blob/master/doc/c2nim.rst) +can parse a large subset of C++ and knows about the `importcpp` pragma pattern language. It is not necessary to know all the details described here. -Similar to the `importc pragma for C -<#foreign-function-interface-importc-pragma>`_, the +Similar to the [importc pragma] for C, the `importcpp` pragma can be used to import `C++`:idx: methods or C++ symbols in general. The generated code then uses the C++ method calling syntax: `obj->method(arg)`:cpp:. In combination with the `header` and `emit` pragmas this allows *sloppy* interfacing with libraries written in C++: -.. code-block:: Nim + ```Nim # Horrible example of how to interface with a C++ engine ... ;-) {.link: "/usr/lib/libIrrlicht.so".} @@ -7345,26 +7622,26 @@ pragmas this allows *sloppy* interfacing with libraries written in C++: header: irr, importcpp: "createDevice(@)".} proc run(device: IrrlichtDevice): bool {. header: irr, importcpp: "#.run(@)".} + ``` The compiler needs to be told to generate C++ (command `cpp`:option:) for this to work. The conditional symbol `cpp` is defined when the compiler emits C++ code. -Namespaces -~~~~~~~~~~ +### Namespaces The *sloppy interfacing* example uses `.emit` to produce `using namespace`:cpp: declarations. It is usually much better to instead refer to the imported name via the `namespace::identifier`:cpp: notation: -.. code-block:: nim + ```nim type IrrlichtDeviceObj {.header: irr, importcpp: "irr::IrrlichtDevice".} = object + ``` -Importcpp for enums -~~~~~~~~~~~~~~~~~~~ +### Importcpp for enums When `importcpp` is applied to an enum type the numerical enum values are annotated with the C++ enum type, like in this example: @@ -7372,8 +7649,7 @@ annotated with the C++ enum type, like in this example: (This turned out to be the simplest way to implement it.) -Importcpp for procs -~~~~~~~~~~~~~~~~~~~ +### Importcpp for procs Note that the `importcpp` variant for procs uses a somewhat cryptic pattern language for maximum flexibility: @@ -7386,30 +7662,34 @@ language for maximum flexibility: For example: -.. code-block:: nim + ```nim proc cppMethod(this: CppObj, a, b, c: cint) {.importcpp: "#.CppMethod(@)".} var x: ptr CppObj cppMethod(x[], 1, 2, 3) + ``` Produces: -.. code-block:: C + ```C x->CppMethod(1, 2, 3) + ``` As a special rule to keep backward compatibility with older versions of the `importcpp` pragma, if there is no special pattern character (any of ``# ' @``) at all, C++'s dot or arrow notation is assumed, so the above example can also be written as: -.. code-block:: nim + ```nim proc cppMethod(this: CppObj, a, b, c: cint) {.importcpp: "CppMethod".} + ``` Note that the pattern language naturally also covers C++'s operator overloading capabilities: -.. code-block:: nim + ```nim proc vectorAddition(a, b: Vec3): Vec3 {.importcpp: "# + #".} proc dictLookup(a: Dict, k: Key): Value {.importcpp: "#[#]".} + ``` - An apostrophe ``'`` followed by an integer ``i`` in the range 0..9 @@ -7421,17 +7701,18 @@ capabilities: For example: -.. code-block:: nim - + ```nim type Input {.importcpp: "System::Input".} = object proc getSubsystem*[T](): ptr T {.importcpp: "SystemManager::getSubsystem<'*0>()", nodecl.} let x: ptr Input = getSubsystem[Input]() + ``` Produces: -.. code-block:: C + ```C x = SystemManager::getSubsystem() + ``` - ``#@`` is a special case to support a `cnew` operation. It is required so @@ -7441,30 +7722,32 @@ Produces: For example C++'s `new`:cpp: operator can be "imported" like this: -.. code-block:: nim + ```nim proc cnew*[T](x: T): ptr T {.importcpp: "(new '*0#@)", nodecl.} # constructor of 'Foo': proc constructFoo(a, b: cint): Foo {.importcpp: "Foo(@)".} let x = cnew constructFoo(3, 4) + ``` Produces: -.. code-block:: C + ```C x = new Foo(3, 4) + ``` However, depending on the use case `new Foo`:cpp: can also be wrapped like this instead: -.. code-block:: nim + ```nim proc newFoo(a, b: cint): ptr Foo {.importcpp: "new Foo(@)".} let x = newFoo(3, 4) + ``` -Wrapping constructors -~~~~~~~~~~~~~~~~~~~~~ +### Wrapping constructors Sometimes a C++ class has a private copy constructor and so code like `Class c = Class(1,2);`:cpp: must not be generated but instead @@ -7473,13 +7756,13 @@ For this purpose the Nim proc that wraps a C++ constructor needs to be annotated with the `constructor`:idx: pragma. This pragma also helps to generate faster C++ code since construction then doesn't invoke the copy constructor: -.. code-block:: nim + ```nim # a better constructor of 'Foo': proc constructFoo(a, b: cint): Foo {.importcpp: "Foo(@)", constructor.} + ``` -Wrapping destructors -~~~~~~~~~~~~~~~~~~~~ +### Wrapping destructors Since Nim generates C++ directly, any destructor is called implicitly by the C++ compiler at the scope exits. This means that often one can get away with @@ -7487,20 +7770,18 @@ not wrapping the destructor at all! However, when it needs to be invoked explicitly, it needs to be wrapped. The pattern language provides everything that is required: -.. code-block:: nim + ```nim proc destroyFoo(this: var Foo) {.importcpp: "#.~Foo()".} + ``` -Importcpp for objects -~~~~~~~~~~~~~~~~~~~~~ +### Importcpp for objects Generic `importcpp`'ed objects are mapped to C++ templates. This means that one can import C++'s templates rather easily without the need for a pattern language for object types: -.. code-block:: nim - :test: "nim cpp $1" - + ```nim test = "nim cpp $1" type StdMap[K, V] {.importcpp: "std::map", header: "".} = object proc `[]=`[K, V](this: var StdMap[K, V]; key: K; val: V) {. @@ -7508,38 +7789,40 @@ language for object types: var x: StdMap[cint, cdouble] x[6] = 91.4 + ``` Produces: -.. code-block:: C + ```C std::map x; x[6] = 91.4; + ``` - If more precise control is needed, the apostrophe `'` can be used in the supplied pattern to denote the concrete type parameters of the generic type. See the usage of the apostrophe operator in proc patterns for more details. - .. code-block:: nim - + ```nim type - VectorIterator {.importcpp: "std::vector<'0>::iterator".} [T] = object + VectorIterator[T] {.importcpp: "std::vector<'0>::iterator".} = object var x: VectorIterator[cint] - + ``` Produces: - .. code-block:: C + ```C std::vector::iterator x; + ``` ImportJs pragma --------------- -Similar to the `importcpp pragma for C++ <#implementation-specific-pragmas-importcpp-pragma>`_, +Similar to the [importcpp pragma] for C++, the `importjs` pragma can be used to import Javascript methods or symbols in general. The generated code then uses the Javascript method calling syntax: ``obj.method(arg)``. @@ -7547,17 +7830,16 @@ calling syntax: ``obj.method(arg)``. ImportObjC pragma ----------------- -Similar to the `importc pragma for C -<#foreign-function-interface-importc-pragma>`_, the `importobjc` pragma can +Similar to the [importc pragma] for C, the `importobjc` pragma can be used to import `Objective C`:idx: methods. The generated code then uses the Objective C method calling syntax: ``[obj method param1: arg]``. In addition with the `header` and `emit` pragmas this allows *sloppy* interfacing with libraries written in Objective C: -.. code-block:: Nim + ```Nim # horrible example of how to interface with GNUStep ... - {.passL: "-lobjc".} + {.passl: "-lobjc".} {.emit: """ #include @interface Greeter:Object @@ -7589,6 +7871,7 @@ allows *sloppy* interfacing with libraries written in Objective C: var g = newGreeter() g.greet(12, 34) g.free() + ``` The compiler needs to be told to generate Objective C (command `objc`:option:) for this to work. The conditional symbol ``objc`` is defined when the compiler @@ -7602,47 +7885,54 @@ The `codegenDecl` pragma can be used to directly influence Nim's code generator. It receives a format string that determines how the variable or proc is declared in the generated code. -For variables, $1 in the format string represents the type of the variable -and $2 is the name of the variable. +For variables, $1 in the format string represents the type of the variable, +$2 is the name of the variable, and each appearance of $# represents $1/$2 +respectively according to its position. The following Nim code: -.. code-block:: nim + ```nim var a {.codegenDecl: "$# progmem $#".}: int + ``` will generate this C code: -.. code-block:: c + ```c int progmem a + ``` For procedures, $1 is the return type of the procedure, $2 is the name of -the procedure, and $3 is the parameter list. +the procedure, $3 is the parameter list, and each appearance of $# represents +$1/$2/$3 respectively according to its position. The following nim code: -.. code-block:: nim + ```nim proc myinterrupt() {.codegenDecl: "__interrupt $# $#$#".} = echo "realistic interrupt handler" + ``` will generate this code: -.. code-block:: c + ```c __interrupt void myinterrupt() + ``` `cppNonPod` pragma ------------------ -The `.cppNonPod` pragma should be used for non-POD `importcpp` types so that they +The `cppNonPod` pragma should be used for non-POD `importcpp` types so that they work properly (in particular regarding constructor and destructor) for -`.threadvar` variables. This requires `--tlsEmulation:off`:option:. +`threadvar` variables. This requires `--tlsEmulation:off`:option:. -.. code-block:: nim + ```nim type Foo {.cppNonPod, importcpp, header: "funs.h".} = object x: cint proc main()= var a {.threadvar.}: Foo + ``` compile-time define pragmas @@ -7662,12 +7952,14 @@ pragma description `booldefine`:idx: Reads in a build-time define as a bool ================= ============================================ -.. code-block:: nim - const FooBar {.intdefine.}: int = 5 - echo FooBar + ```nim + const FooBar {.intdefine.}: int = 5 + echo FooBar + ``` -.. code:: cmd - nim c -d:FooBar=42 foobar.nim + ```cmd + nim c -d:FooBar=42 foobar.nim + ``` In the above example, providing the `-d`:option: flag causes the symbol `FooBar` to be overwritten at compile-time, printing out 42. If the @@ -7691,7 +7983,7 @@ They cannot be imported from a module. Example: -.. code-block:: nim + ```nim when appType == "lib": {.pragma: rtl, exportc, dynlib, cdecl.} else: @@ -7699,6 +7991,7 @@ Example: proc p*(a, b: int): int {.rtl.} = result = a + b + ``` In the example, a new pragma named `rtl` is introduced that either imports a symbol from a dynamic library or exports the symbol for dynamic library @@ -7711,17 +8004,18 @@ It is possible to define custom typed pragmas. Custom pragmas do not affect code generation directly, but their presence can be detected by macros. Custom pragmas are defined using templates annotated with pragma `pragma`: -.. code-block:: nim + ```nim template dbTable(name: string, table_space: string = "") {.pragma.} template dbKey(name: string = "", primary_key: bool = false) {.pragma.} template dbForeignKey(t: typedesc) {.pragma.} template dbIgnore {.pragma.} + ``` Consider this stylized example of a possible Object Relation Mapping (ORM) implementation: -.. code-block:: nim + ```nim const tblspace {.strdefine.} = "dev" # switch for dev, test and prod environments type @@ -7736,7 +8030,8 @@ implementation: user_id {.dbForeignKey: User.}: int read_access: bool write_access: bool - admin_acess: bool + admin_access: bool + ``` In this example, custom pragmas are used to describe how Nim objects are mapped to the schema of the relational database. Custom pragmas can have @@ -7751,7 +8046,7 @@ definitions, statements, etc. The macros module includes helpers which can be used to simplify custom pragma access `hasCustomPragma`, `getCustomPragmaVal`. Please consult the -`macros `_ module documentation for details. These macros are not +[macros](macros.html) module documentation for details. These macros are not magic, everything they do can also be achieved by walking the AST of the object representation. @@ -7759,50 +8054,56 @@ More examples with custom pragmas: - Better serialization/deserialization control: - .. code-block:: nim + ```nim type MyObj = object a {.dontSerialize.}: int b {.defaultDeserialize: 5.}: int c {.serializationKey: "_c".}: string + ``` - Adopting type for gui inspector in a game engine: - .. code-block:: nim + ```nim type MyComponent = object position {.editable, animatable.}: Vector3 alpha {.editRange: [0.0..1.0], animatable.}: float32 + ``` Macro pragmas ------------- Macros and templates can sometimes be called with the pragma syntax. Cases -where this is possible include when attached to routine (procs, iterators, etc) +where this is possible include when attached to routine (procs, iterators, etc.) declarations or routine type expressions. The compiler will perform the following simple syntactic transformations: -.. code-block:: nim + ```nim template command(name: string, def: untyped) = discard proc p() {.command("print").} = discard + ``` This is translated to: -.. code-block:: nim + ```nim command("print"): proc p() = discard + ``` ------ -.. code-block:: nim + ```nim type AsyncEventHandler = proc (x: Event) {.async.} + ``` This is translated to: -.. code-block:: nim + ```nim type AsyncEventHandler = async(proc (x: Event)) + ``` ------ @@ -7813,8 +8114,8 @@ the same way. There are a few more applications of macro pragmas, such as in type, variable and constant declarations, but this behavior is considered to be -experimental and is documented in the `experimental manual -` instead. +experimental and is documented in the [experimental manual]( +manual_experimental.html#extended-macro-pragmas) instead. Foreign function interface @@ -7832,30 +8133,35 @@ from C. The optional argument is a string containing the C identifier. If the argument is missing, the C name is the Nim identifier *exactly as spelled*: -.. code-block:: + ```nim proc printf(formatstr: cstring) {.header: "", importc: "printf", varargs.} + ``` When `importc` is applied to a `let` statement it can omit its value which will then be expected to come from C. This can be used to import a C `const`:c:\: -.. code-block:: + ```nim {.emit: "const int cconst = 42;".} let cconst {.importc, nodecl.}: cint assert cconst == 42 + ``` Note that this pragma has been abused in the past to also work in the JS backend for JS objects and functions. Other backends do provide the same feature under the same name. Also, when the target language is not set to C, other pragmas are available: - * `importcpp `_ - * `importobjc `_ - * `importjs `_ + * [importcpp][importcpp pragma] + * [importobjc][importobjc pragma] + * [importjs][importjs pragma] + +The string literal passed to `importc` can be a format string: -.. code-block:: Nim + ```Nim proc p(s: cstring) {.importc: "prefix$1".} + ``` In the example, the external name of `p` is set to `prefixp`. Only ``$1`` is available and a literal dollar sign must be written as ``$$``. @@ -7868,24 +8174,26 @@ procedure to C. Enums and constants can't be exported. The optional argument is a string containing the C identifier. If the argument is missing, the C name is the Nim identifier *exactly as spelled*: -.. code-block:: Nim + ```Nim proc callme(formatstr: cstring) {.exportc: "callMe", varargs.} + ``` Note that this pragma is somewhat of a misnomer: Other backends do provide the same feature under the same name. The string literal passed to `exportc` can be a format string: -.. code-block:: Nim + ```Nim proc p(s: string) {.exportc: "prefix$1".} = echo s + ``` In the example, the external name of `p` is set to `prefixp`. Only ``$1`` is available and a literal dollar sign must be written as ``$$``. If the symbol should also be exported to a dynamic library, the `dynlib` pragma should be used in addition to the `exportc` pragma. See -`Dynlib pragma for export <#foreign-function-interface-dynlib-pragma-for-export>`_. +[Dynlib pragma for export]. Extern pragma @@ -7893,9 +8201,10 @@ Extern pragma Like `exportc` or `importc`, the `extern` pragma affects name mangling. The string literal passed to `extern` can be a format string: -.. code-block:: Nim + ```Nim proc p(s: string) {.extern: "prefix$1".} = echo s + ``` In the example, the external name of `p` is set to `prefixp`. Only ``$1`` is available and a literal dollar sign must be written as ``$$``. @@ -7907,10 +8216,11 @@ Bycopy pragma The `bycopy` pragma can be applied to an object or tuple type and instructs the compiler to pass the type by value to procs: -.. code-block:: nim + ```nim type Vector {.bycopy.} = object x, y, z: float + ``` The Nim compiler automatically determines whether a parameter is passed by value or by reference based on the parameter type's size. If a parameter must be passed by value or by reference, (such as when interfacing with a C library) use the bycopy or byref pragmas. @@ -7928,16 +8238,17 @@ types). It tells Nim that the proc can take a variable number of parameters after the last specified parameter. Nim string values will be converted to C strings automatically: -.. code-block:: Nim + ```Nim proc printf(formatstr: cstring) {.nodecl, varargs.} printf("hallo %s", "world") # "world" will be passed as C string + ``` Union pragma ------------ The `union` pragma can be applied to any `object` type. It means all -of the object's fields are overlaid in memory. This produces a `union`:c: +of an object's fields are overlaid in memory. This produces a `union`:c: instead of a `struct`:c: in the generated C/C++ code. The object declaration then must not use inheritance or any GC'ed memory but this is currently not checked. @@ -7964,9 +8275,10 @@ With the `dynlib` pragma, a procedure or a variable can be imported from a dynamic library (``.dll`` files for Windows, ``lib*.so`` files for UNIX). The non-optional argument has to be the name of the dynamic library: -.. code-block:: Nim + ```Nim proc gtk_image_new(): PGtkWidget {.cdecl, dynlib: "libgtk-x11-2.0.so", importc.} + ``` In general, importing a dynamic library does not require any special linker options or linking with import libraries. This also implies that no *devel* @@ -7974,9 +8286,10 @@ packages need to be installed. The `dynlib` import mechanism supports a versioning scheme: -.. code-block:: nim + ```nim proc Tcl_Eval(interp: pTcl_Interp, script: cstring): int {.cdecl, importc, dynlib: "libtcl(|8.5|8.4|8.3).so.(1|0)".} + ``` At runtime, the dynamic library is searched for (in this order):: @@ -7992,7 +8305,7 @@ At runtime, the dynamic library is searched for (in this order):: The `dynlib` pragma supports not only constant strings as an argument but also string expressions in general: -.. code-block:: nim + ```nim import std/os proc getDllName: string = @@ -8003,6 +8316,7 @@ string expressions in general: quit("could not load dynamic library") proc myImport(s: cstring) {.cdecl, importc, dynlib: getDllName().} + ``` **Note**: Patterns like ``libtcl(|8.5|8.4).so`` are only supported in constant strings, because they are precompiled. @@ -8012,7 +8326,7 @@ because of order of initialization problems. **Note**: A `dynlib` import can be overridden with the `--dynlibOverride:name`:option: command-line option. The -`Compiler User Guide `_ contains further information. +[Compiler User Guide](nimc.html) contains further information. Dynlib pragma for export @@ -8022,8 +8336,9 @@ With the `dynlib` pragma, a procedure can also be exported to a dynamic library. The pragma then has no argument and has to be used in conjunction with the `exportc` pragma: -.. code-block:: Nim + ```Nim proc exportme(): int {.cdecl, exportc, dynlib.} + ``` This is only useful if the program is compiled as a dynamic library via the `--app:lib`:option: command-line option. @@ -8034,10 +8349,10 @@ Threads ======= To enable thread support the `--threads:on`:option: command-line switch needs to -be used. The system_ module then contains several threading primitives. -See the `channels `_ modules +be used. The [system module](system.html) module then contains several threading primitives. +See the [channels](channels_builtin.html) modules for the low-level thread API. There are also high-level parallelism constructs -available. See `spawn `_ for +available. See [spawn](manual_experimental.html#parallel-amp-spawn) for further details. Nim's memory model for threads is quite different than that of other common @@ -8060,8 +8375,7 @@ violations of the `no heap sharing restriction`:idx:\: This restriction implies that it is invalid to construct a data structure that consists of memory allocated from different (thread-local) heaps. -A thread proc is passed to `createThread` or `spawn` and invoked -indirectly; so the `thread` pragma implies `procvar`. +A thread proc can be passed to `createThread` or `spawn`. @@ -8072,8 +8386,9 @@ A variable can be marked with the `threadvar` pragma, which makes it a `thread-local`:idx: variable; Additionally, this implies all the effects of the `global` pragma. -.. code-block:: nim + ```nim var checkpoints* {.threadvar.}: seq[string] + ``` Due to implementation restrictions, thread-local variables cannot be initialized within the `var` section. (Every thread-local variable needs to @@ -8105,21 +8420,21 @@ pragmas: Guards and locks sections ------------------------- -Protecting global variables -~~~~~~~~~~~~~~~~~~~~~~~~~~~ +### Protecting global variables Object fields and global variables can be annotated via a `guard` pragma: -.. code-block:: nim + ```nim + import std/locks - var glock: TLock + var glock: Lock var gdata {.guard: glock.}: int + ``` The compiler then ensures that every access of `gdata` is within a `locks` section: -.. code-block:: nim - + ```nim proc invalid = # invalid: unguarded access: echo gdata @@ -8128,6 +8443,7 @@ section: # valid access: {.locks: [glock].}: echo gdata + ``` Top level accesses to `gdata` are always allowed so that it can be initialized conveniently. It is *assumed* (but not enforced) that every top level statement @@ -8137,22 +8453,21 @@ The `locks` section deliberately looks ugly because it has no runtime semantics and should not be used directly! It should only be used in templates that also implement some form of locking at runtime: -.. code-block:: nim - - template lock(a: TLock; body: untyped) = + ```nim + template lock(a: Lock; body: untyped) = pthread_mutex_lock(a) {.locks: [a].}: try: body finally: pthread_mutex_unlock(a) + ``` The guard does not need to be of any particular type. It is flexible enough to model low level lockfree mechanisms: -.. code-block:: nim - + ```nim var dummyLock {.compileTime.}: int var atomicCounter {.guard: dummyLock.}: int @@ -8162,40 +8477,42 @@ model low level lockfree mechanisms: x echo atomicRead(atomicCounter) + ``` The `locks` pragma takes a list of lock expressions `locks: [a, b, ...]` in order to support *multi lock* statements. Why these are essential is -explained in the `lock levels <#guards-and-locks-lock-levels>`_ section. +explained in the [lock levels](manual_experimental.md#lock-levels) section +of experimental manual. -Protecting general locations -~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +### Protecting general locations The `guard` annotation can also be used to protect fields within an object. The guard then needs to be another field within the same object or a global variable. Since objects can reside on the heap or on the stack, this greatly enhances -the expressivity of the language: +the expressiveness of the language: -.. code-block:: nim + ```nim + import std/locks type ProtectedCounter = object v {.guard: L.}: int - L: TLock + L: Lock proc incCounters(counters: var openArray[ProtectedCounter]) = for i in 0..counters.high: lock counters[i].L: inc counters[i].v + ``` The access to field `x.v` is allowed since its guard `x.L` is active. After template expansion, this amounts to: -.. code-block:: nim - + ```nim proc incCounters(counters: var openArray[ProtectedCounter]) = for i in 0..counters.high: pthread_mutex_lock(counters[i].L) @@ -8204,6 +8521,7 @@ After template expansion, this amounts to: inc counters[i].v finally: pthread_mutex_unlock(counters[i].L) + ``` There is an analysis that checks that `counters[i].L` is the lock that corresponds to the protected location `counters[i].v`. This analysis is called @@ -8215,8 +8533,8 @@ Two paths are considered equivalent if they are syntactically the same. This means the following compiles (for now) even though it really should not: -.. code-block:: nim - + ```nim {.locks: [a[i].L].}: inc i access a[i].v + ``` diff --git a/doc/manual/var_t_return.rst b/doc/manual/var_t_return.md similarity index 97% rename from doc/manual/var_t_return.rst rename to doc/manual/var_t_return.md index f5c5bc4c0b61..15d908c74be7 100644 --- a/doc/manual/var_t_return.rst +++ b/doc/manual/var_t_return.md @@ -6,7 +6,7 @@ rule: If `result` does not refer to a location pointing to the heap (that is in `result = X` the `X` involves a `ptr` or `ref` access) then it has to be derived from the routine's first parameter: -.. code-block:: nim + ```nim proc forward[T](x: var T): var T = result = x # ok, derived from the first parameter. @@ -17,6 +17,7 @@ then it has to be derived from the routine's first parameter: result = forward(x) # Error: location is derived from `x` # which is not p's first parameter and lives # on the stack. + ``` In other words, the lifetime of what `result` points to is attached to the lifetime of the first parameter and that is enough knowledge to verify diff --git a/doc/manual_experimental.rst b/doc/manual_experimental.md similarity index 93% rename from doc/manual_experimental.rst rename to doc/manual_experimental.md index 3089755cbb1a..60461cb6d55d 100644 --- a/doc/manual_experimental.rst +++ b/doc/manual_experimental.md @@ -30,17 +30,16 @@ The `void` type denotes the absence of any type. Parameters of type `void` are treated as non-existent, `void` as a return type means that the procedure does not return a value: -.. code-block:: nim - + ```nim proc nothing(x, y: void): void = echo "ha" nothing() # writes "ha" to stdout + ``` The `void` type is particularly useful for generic code: -.. code-block:: nim - + ```nim proc callProc[T](p: proc (x: T), x: T) = when T is void: p() @@ -52,78 +51,77 @@ The `void` type is particularly useful for generic code: callProc[int](intProc, 12) callProc[void](emptyProc) + ``` However, a `void` type cannot be inferred in generic code: -.. code-block:: nim - + ```nim callProc(emptyProc) # Error: type mismatch: got (proc ()) # but expected one of: # callProc(p: proc (T), x: T) + ``` The `void` type is only valid for parameters and return types; other symbols cannot have the type `void`. +Top-down type inference +======================= -Unicode Operators -================= - -Under the `--experimental:unicodeOperators`:option: switch, -these Unicode operators are also parsed as operators:: - - ∙ ∘ × ★ ⊗ ⊘ ⊙ ⊛ ⊠ ⊡ ∩ ∧ ⊓ # same priority as * (multiplication) - ± ⊕ ⊖ ⊞ ⊟ ∪ ∨ ⊔ # same priority as + (addition) - +In expressions such as: -If enabled, Unicode operators can be combined with non-Unicode operator -symbols. The usual precedence extensions then apply, for example, `⊠=` is an -assignment like operator just like `*=` is. +```nim +let a: T = ex +``` -No Unicode normalization step is performed. +Normally, the compiler type checks the expression `ex` by itself, then +attempts to statically convert the type-checked expression to the given type +`T` as much as it can, while making sure it matches the type. The extent of +this process is limited however due to the expression usually having +an assumed type that might clash with the given type. -.. note:: Due to parser limitations one **cannot** enable this feature via a - pragma `{.experimental: "unicodeOperators".}` reliably. +With top-down type inference, the expression is type checked with the +extra knowledge that it is supposed to be of type `T`. For example, +the following code is does not compile with the former method, but +compiles with top-down type inference: +```nim +let foo: (float, uint8, cstring) = (1, 2, "abc") +``` -Overloadable enum value names -============================= +The tuple expression has an expected type of `(float, uint8, cstring)`. +Since it is a tuple literal, we can use this information to assume the types +of its elements. The expected types for the expressions `1`, `2` and `"abc"` +are respectively `float`, `uint8`, and `cstring`; and these expressions can be +statically converted to these types. -Enabled via `{.experimental: "overloadableEnums".}`. +Without this information, the type of the tuple expression would have been +assumed to be `(int, int, string)`. Thus the type of the tuple expression +would not match the type of the variable, and an error would be given. -Enum value names are overloadable, much like routines. If both of the enums -`T` and `U` have a member named `foo`, then the identifier `foo` corresponds -to a choice between `T.foo` and `U.foo`. During overload resolution, -the correct type of `foo` is decided from the context. If the type of `foo` is -ambiguous, a static error will be produced. +The extent of this varies, but there are some notable special cases. -.. code-block:: nim - :test: "nim c $1" +Sequence literals +----------------- - {.experimental: "overloadableEnums".} +Top-down type inference applies to sequence literals. - type - E1 = enum - value1, - value2 - E2 = enum - value1, - value2 = 4 +```nim +let x: seq[seq[float]] = @[@[1, 2, 3], @[4, 5, 6]] +``` - const - Lookuptable = [ - E1.value1: "1", - # no need to qualify value2, known to be E1.value2 - value2: "2" - ] +This behavior is tied to the `@` overloads in the `system` module, +so overloading `@` can disable this behavior. This can be circumvented by +specifying the `` system.`@` `` overload. - proc p(e: E1) = - # disambiguation in 'case' statements: - case e - of value1: echo "A" - of value2: echo "B" +```nim +proc `@`(x: string): string = "@" & x - p value2 +# does not compile: +let x: seq[float] = @[1, 2, 3] +# compiles: +let x: seq[float] = system.`@`([1, 2, 3]) +``` Package level objects @@ -144,8 +142,7 @@ available. Example: -.. code-block:: nim - + ```nim # module A (in an arbitrary package) type Pack.SomeObject = object # declare as incomplete object of package 'Pack' @@ -154,15 +151,16 @@ Example: # Incomplete objects can be used as parameters: proc myproc(x: SomeObject) = discard + ``` -.. code-block:: nim - + ```nim # module B (in package "Pack") type SomeObject* {.package.} = object # Use 'package' to complete the object s, t: string x, y: int + ``` This feature will likely be superseded in the future by support for recursive module dependencies. @@ -176,7 +174,7 @@ from a module. The syntax `import foo {.all.}` can be used to import all symbols from the module `foo`. Note that importing private symbols is generally not recommended. -See also the experimental `importutils `_ module. +See also the experimental [importutils](importutils.html) module. Code reordering @@ -223,8 +221,7 @@ preface definitions inside a module. Example: -.. code-block:: nim - + ```nim {.experimental: "codeReordering".} proc foo(x: int) = @@ -234,14 +231,14 @@ Example: echo(x) foo(10) + ``` Variables can also be reordered as well. Variables that are *initialized* (i.e. variables that have their declaration and assignment combined in a single statement) can have their entire initialization statement reordered. Be wary of what code is executed at the top level: -.. code-block:: nim - + ```nim {.experimental: "codeReordering".} proc a() = @@ -250,6 +247,7 @@ what code is executed at the top level: var foo = 5 a() # outputs: "5" + ``` .. TODO: Let's table this for now. This is an *experimental feature* and so the @@ -260,8 +258,7 @@ what code is executed at the top level: code reordering process, and not after. As an example, the output of this code is the same as it would be with code reordering disabled. - .. code-block:: nim - + ```nim {.experimental: "codeReordering".} proc x() = @@ -270,12 +267,12 @@ what code is executed at the top level: var foo = 4 x() # "false" + ``` It is important to note that reordering *only* works for symbols at top level scope. Therefore, the following will *fail to compile:* -.. code-block:: nim - + ```nim {.experimental: "codeReordering".} proc a() = @@ -284,6 +281,7 @@ scope. Therefore, the following will *fail to compile:* echo("Hello!") a() + ``` This feature will likely be replaced with a better solution to remove the need for forward declarations. @@ -295,8 +293,7 @@ Automatic dereferencing Automatic dereferencing is performed for the first argument of a routine call. This feature has to be enabled via `{.experimental: "implicitDeref".}`: -.. code-block:: nim - + ```nim {.experimental: "implicitDeref".} type @@ -309,6 +306,7 @@ This feature has to be enabled via `{.experimental: "implicitDeref".}`: let n = Node() echo n.depth # no need to write n[].depth + ``` Special Operators @@ -333,21 +331,21 @@ for a dot operator that can be matched against a re-written form of the expression, where the unknown field or proc name is passed to an `untyped` parameter: -.. code-block:: nim - + ```nim a.b # becomes `.`(a, b) a.b(c, d) # becomes `.`(a, b, c, d) + ``` The matched dot operators can be symbols of any callable kind (procs, templates and macros), depending on the desired effect: -.. code-block:: nim - + ```nim template `.`(js: PJsonNode, field: untyped): JSON = js[astToStr(field)] var js = parseJson("{ x: 1, y: 2}") echo js.x # outputs 1 echo js.y # outputs 2 + ``` The following dot operators are available: @@ -366,9 +364,9 @@ operator `.=` ------------- This operator will be matched against assignments to missing fields. -.. code-block:: nim - + ```nim a.b = c # becomes `.=`(a, b, c) + ``` Call operator ------------- @@ -377,8 +375,7 @@ precedence over dot operators, however it does not match missing overloads for existing routines. The experimental `callOperator` switch must be enabled to use this operator. -.. code-block:: nim - + ```nim {.experimental: "callOperator".} template `()`(a: int, b: float): untyped = $(a, b) @@ -402,19 +399,21 @@ to use this operator. doAssert not compiles(a.b(c)) # gives a type mismatch error same as b(a, c) doAssert (a.b)(c) == `()`(a.b, c) + ``` Extended macro pragmas ====================== -Macro pragmas as described in `the manual `_ +Macro pragmas as described in [the manual](manual.html#userminusdefined-pragmas-macro-pragmas) can also be applied to type, variable and constant declarations. For types: -.. code-block:: nim + ```nim type MyObject {.schema: "schema.protobuf".} = object + ``` This is translated to a call to the `schema` macro with a `nnkTypeDef` AST node capturing the left-hand side, remaining pragmas and the right-hand @@ -437,19 +436,21 @@ For variables and constants, it is largely the same, except a unary node with the same kind as the section containing a single definition is passed to macros, and macros can return any expression. -.. code-block:: nim + ```nim var a = ... b {.importc, foo, nodecl.} = ... c = ... + ``` Assuming `foo` is a macro or a template, this is roughly equivalent to: -.. code-block:: nim + ```nim var a = ... foo: var b {.importc, nodecl.} = ... var c = ... + ``` Symbols as template/macro calls @@ -459,7 +460,7 @@ Templates and macros that take no arguments can be called as lone symbols, i.e. without parentheses. This is useful for repeated uses of complex expressions that cannot conveniently be represented as runtime values. -.. code-block:: nim + ```nim type Foo = object bar: int @@ -468,6 +469,7 @@ expressions that cannot conveniently be represented as runtime values. assert bar == 10 bar = 15 assert bar == 15 + ``` In the future, this may require more specific information on template or macro signatures to be used. Specializations for some applications of this may also @@ -483,8 +485,7 @@ Not nil annotation All types for which `nil` is a valid value can be annotated with the `not nil` annotation to exclude `nil` as a valid value: -.. code-block:: nim - + ```nim {.experimental: "notnil".} type @@ -500,12 +501,13 @@ All types for which `nil` is a valid value can be annotated with the # and also this: var x: PObject p(x) + ``` The compiler ensures that every code path initializes variables which contain non-nilable pointers. The details of this analysis are still to be specified here. -.. include:: manual_experimental_strictnotnil.rst +.. include:: manual_experimental_strictnotnil.md Aliasing restrictions in parameter passing @@ -545,8 +547,7 @@ via a parameter that is not declared as a `var` parameter. For example: -.. code-block:: nim - + ```nim {.experimental: "strictFuncs".} type @@ -566,10 +567,11 @@ For example: m.data = "yeah" # the mutation is here # Error: 'mut' can have side effects # an object reachable from 'n' is potentially mutated + ``` The algorithm behind this analysis is described in -the `view types section <#view-types-algorithm>`_. +the [view types algorithm]. View types @@ -585,23 +587,23 @@ A view type is a type that is or contains one of the following types: For example: -.. code-block:: nim - + ```nim type View1 = openArray[byte] View2 = lent string View3 = Table[openArray[char], int] + ``` Exceptions to this rule are types constructed via `ptr` or `proc`. For example, the following types are **not** view types: -.. code-block:: nim - + ```nim type NotView1 = proc (x: openArray[int]) NotView2 = ptr openArray[char] NotView3 = ptr array[4, lent int] + ``` The mutability aspect of a view type is not part of the type but part @@ -618,8 +620,7 @@ it was borrowed from. For example: -.. code-block:: nim - + ```nim {.experimental: "views".} proc take(a: openArray[int]) = @@ -641,6 +642,7 @@ For example: main(@[11, 22, 33]) + ``` A local variable of a view type can borrow from a location @@ -683,7 +685,7 @@ has `source` as the owner. A path expression `e` is defined recursively: If a view type is used as a return type, the location must borrow from a location that is derived from the first parameter that is passed to the proc. -See `the manual `_ +See [the manual](manual.html#procedures-var-return-type) for details about how this is done for `var T`. A mutable view can borrow from a mutable location, an immutable view can borrow @@ -699,8 +701,7 @@ For the duration of the borrow operation, no mutations to the borrowed locations may be performed except via the view that borrowed from the location. The borrowed location is said to be *sealed* during the borrow. -.. code-block:: nim - + ```nim {.experimental: "views".} type @@ -711,32 +712,33 @@ location. The borrowed location is said to be *sealed* during the borrow. let v: lent Obj = s[0] # seal 's' s.setLen 0 # prevented at compile-time because 's' is sealed. echo v.field + ``` The scope of the view does not matter: -.. code-block:: nim - + ```nim proc valid(s: var seq[Obj]) = let v: lent Obj = s[0] # begin of borrow echo v.field # end of borrow s.setLen 0 # valid because 'v' isn't used afterwards + ``` The analysis requires as much precision about mutations as is reasonably obtainable, -so it is more effective with the experimental `strict funcs <#strict-funcs>`_ +so it is more effective with the experimental [strict funcs] feature. In other words `--experimental:views`:option: works better with `--experimental:strictFuncs`:option:. The analysis is currently control flow insensitive: -.. code-block:: nim - + ```nim proc invalid(s: var seq[Obj]) = let v: lent Obj = s[0] if false: s.setLen 0 echo v.field + ``` In this example, the compiler assumes that `s.setLen 0` invalidates the borrow operation of `v` even though a human being can easily see that it @@ -824,8 +826,7 @@ arbitrary set of requirements that the matched type must satisfy. Concepts are written in the following form: -.. code-block:: nim - + ```nim type Comparable = concept x, y (x < y) is bool @@ -838,6 +839,7 @@ Concepts are written in the following form: for value in s: value is T + ``` The concept matches if: @@ -850,29 +852,28 @@ as `var`, `ref`, `ptr` and `static` to denote a more specific type of instance. You can also apply the `type` modifier to create a named instance of the type itself: -.. code-block:: nim - + ```nim type MyConcept = concept x, var v, ref r, ptr p, static s, type T ... + ``` Within the concept body, types can appear in positions where ordinary values and parameters are expected. This provides a more convenient way to check for the presence of callable symbols with specific signatures: -.. code-block:: nim - + ```nim type OutputStream = concept var s s.write(string) + ``` In order to check for symbols accepting `type` params, you must prefix the type with the explicit `type` modifier. The named instance of the type, following the `concept` keyword is also considered to have the explicit modifier and will be matched only as a type. -.. code-block:: nim - + ```nim type # Let's imagine a user-defined casting framework with operators # such as `val.to(string)` and `val.to(JSonValue)`. We can test @@ -890,6 +891,7 @@ explicit modifier and will be matched only as a type. x is AdditiveMonoid -x is T x - y is T + ``` Please note that the `is` operator allows one to easily verify the precise type signatures of the required operations, but since type inference and @@ -909,12 +911,12 @@ When you need to understand why the compiler is not matching a particular concept and, as a result, a wrong overload is selected, you can apply the `explain` pragma to either the concept body or a particular call-site. -.. code-block:: nim - + ```nim type MyConcept {.explain.} = concept ... overloadedProc(x, y, z) {.explain.} + ``` This will provide Hints in the compiler output either every time the concept is not matched or only on the particular call-site. @@ -925,8 +927,7 @@ Generic concepts and type binding rules The concept types can be parametric just like the regular generic types: -.. code-block:: nim - + ```nim ### matrixalgo.nim import std/typetraits @@ -986,6 +987,7 @@ The concept types can be parametric just like the regular generic types: echo m.transposed.determinant setPerspectiveProjection projectionMatrix + ``` When the concept type is matched against a concrete type, the unbound type parameters are inferred from the body of the concept in a way that closely @@ -999,11 +1001,11 @@ and `x.data is seq[T]`. Unbound static params will be inferred from expressions involving the `==` operator and also when types dependent on them are being matched: -.. code-block:: nim - + ```nim type MatrixReducer[M, N: static int; T] = concept x x.reduce(SquareMatrix[N, T]) is array[M, int] + ``` The Nim compiler includes a simple linear equation solver, allowing it to infer static params in some situations where integer arithmetic is involved. @@ -1014,8 +1016,7 @@ modifier to any of the otherwise inferable types to get a type that will be matched without permanently inferring it. This may be useful when you need to match several procs accepting the same wide class of types: -.. code-block:: nim - + ```nim type Enumerable[T] = concept e for v in e: @@ -1032,13 +1033,13 @@ to match several procs accepting the same wide class of types: # it's also possible to give an alias name to a `bind many` type class type Enum = distinct Enumerable o.baz is Enum + ``` On the other hand, using `bind once` types allows you to test for equivalent types used in multiple signatures, without actually requiring any concrete types, thus allowing you to encode implementation-defined types: -.. code-block:: nim - + ```nim type MyConcept = concept x type T1 = auto @@ -1049,6 +1050,7 @@ types, thus allowing you to encode implementation-defined types: x.alpha(T2) x.omega(T2) # both procs must accept the same type # and it must be a numeric sequence + ``` As seen in the previous examples, you can refer to generic concepts such as `Enumerable[T]` just by their short name. Much like the regular generic types, @@ -1066,9 +1068,7 @@ in any required way. For example, here is how one might define the classic `Functor` concept from Haskell and then demonstrate that Nim's `Option[T]` type is an instance of it: -.. code-block:: nim - :test: "nim c $1" - + ```nim test = "nim c $1" import std/[sugar, typetraits] type @@ -1089,6 +1089,7 @@ type is an instance of it: import std/options echo Option[int] is Functor # prints true + ``` Concept derived values @@ -1098,8 +1099,7 @@ All top level constants or types appearing within the concept body are accessible through the dot operator in procs where the concept was successfully matched to a concrete type: -.. code-block:: nim - + ```nim type DateTime = concept t1, t2, type T const Min = T.MinDate @@ -1121,6 +1121,7 @@ matched to a concrete type: deviation: float ... + ``` Concept refinement @@ -1133,8 +1134,7 @@ overload resolution, Nim will assign a higher precedence to the most specific one. As an alternative way of defining concept refinements, you can use the object inheritance syntax involving the `of` keyword: -.. code-block:: nim - + ```nim type Graph = concept g, type G of EquallyComparable, Copyable type @@ -1168,6 +1168,7 @@ object inheritance syntax involving the `of` keyword: proc f(g: IncidendeGraph) proc f(g: BidirectionalGraph) # this one will be preferred if we pass a type # matching the BidirectionalGraph concept + ``` .. Converter type classes @@ -1177,8 +1178,7 @@ object inheritance syntax involving the `of` keyword: a small set of simpler types. This is achieved with a `return` statement within the concept body: - .. code-block:: nim - + ```nim type Stringable = concept x $x is string @@ -1202,6 +1202,7 @@ object inheritance syntax involving the `of` keyword: # the same call at the cost of additional instantiations # the varargs param will be converted to a tuple proc log(format: static string, varargs[distinct StringRef]) + ``` .. @@ -1229,8 +1230,7 @@ object inheritance syntax involving the `of` keyword: a converter type class, which converts the regular instances of the matching types to the corresponding VTable type. - .. code-block:: nim - + ```nim type IntEnumerable = vtref Enumerable[int] @@ -1243,6 +1243,7 @@ object inheritance syntax involving the `of` keyword: proc addStream(o: var MyObject, e: OutputStream.vtref) = o.streams.add e + ``` The procs that will be included in the vtable are derived from the concept body and include all proc calls for which all param types were specified as @@ -1272,15 +1273,15 @@ object inheritance syntax involving the `of` keyword: The signature has to be: - .. code-block:: nim - + ```nim proc `=deepCopy`(x: T): T + ``` This mechanism will be used by most data structures that support shared memory, like channels, to implement thread safe automatic memory management. The builtin `deepCopy` can even clone closures and their environments. See - the documentation of `spawn <#parallel-amp-spawn-spawn-statement>`_ for details. + the documentation of [spawn][spawn statement] for details. Dynamic arguments for bindSym @@ -1289,7 +1290,7 @@ Dynamic arguments for bindSym This experimental feature allows the symbol name argument of `macros.bindSym` to be computed dynamically. -.. code-block:: nim + ```nim {.experimental: "dynamicBindSym".} import macros @@ -1299,6 +1300,7 @@ to be computed dynamically. echo callOp("+", 1, 2) echo callOp("-", 5, 4) + ``` Term rewriting macros @@ -1309,12 +1311,12 @@ a *name* but also a *pattern* that is searched for after the semantic checking phase of the compiler: This means they provide an easy way to enhance the compilation pipeline with user defined optimizations: -.. code-block:: nim - + ```nim template optMul{`*`(a, 2)}(a: int): int = a + a let x = 3 echo x * 2 + ``` The compiler now rewrites `x * 2` as `x + x`. The code inside the curly brackets is the pattern to match against. The operators `*`, `**`, @@ -1332,8 +1334,7 @@ Once this limit has been passed, the term rewriting macro will be ignored. Unfortunately optimizations are hard to get right and even this tiny example is **wrong**: -.. code-block:: nim - + ```nim template optMul{`*`(a, 2)}(a: int): int = a + a proc f(): int = @@ -1341,12 +1342,12 @@ is **wrong**: result = 55 echo f() * 2 + ``` We cannot duplicate 'a' if it denotes an expression that has a side effect! Fortunately Nim supports side effect analysis: -.. code-block:: nim - + ```nim template optMul{`*`(a, 2)}(a: int{noSideEffect}): int = a + a proc f(): int = @@ -1354,6 +1355,7 @@ Fortunately Nim supports side effect analysis: result = 55 echo f() * 2 # not optimized ;-) + ``` You can make one overload matching with a constraint and one without, and the one with a constraint will have precedence, and so you can handle both cases @@ -1363,15 +1365,15 @@ So what about `2 * a`? We should tell the compiler `*` is commutative. We cannot really do that however as the following code only swaps arguments blindly: -.. code-block:: nim - + ```nim template mulIsCommutative{`*`(a, b)}(a, b: int): int = b * a + ``` What optimizers really need to do is a *canonicalization*: -.. code-block:: nim - + ```nim template canonMul{`*`(a, b)}(a: int{lit}, b: int): int = b * a + ``` The `int{lit}` parameter pattern matches against an expression of type `int`, but only if it's a literal. @@ -1429,17 +1431,16 @@ The `alias` and `noalias` predicates refer not only to the matching AST, but also to every other bound parameter; syntactically they need to occur after the ordinary AST predicates: -.. code-block:: nim - + ```nim template ex{a = b + c}(a: int{noalias}, b, c: int) = # this transformation is only valid if 'b' and 'c' do not alias 'a': a = b inc a, c + ``` Another example: -.. code-block:: nim - + ```nim proc somefunc(s: string) = assert s == "variable" proc somefunc(s: string{nkStrLit}) = assert s == "literal" proc somefunc(s: string{nkRStrLit}) = assert s == r"raw" @@ -1454,6 +1455,7 @@ Another example: somefunc("literal") somefunc(r"raw") somefunc("""triple""") + ``` Pattern operators @@ -1463,26 +1465,25 @@ The operators `*`, `**`, `|`, `~` have a special meaning in patterns if they are written in infix notation. -The `|` operator -~~~~~~~~~~~~~~~~~~ +### The `|` operator The `|` operator if used as infix operator creates an ordered choice: -.. code-block:: nim - + ```nim template t{0|1}(): untyped = 3 let a = 1 # outputs 3: echo a + ``` The matching is performed after the compiler performed some optimizations like constant folding, so the following does not work: -.. code-block:: nim - + ```nim template t{0|1}(): untyped = 3 # outputs 1: echo 1 + ``` The reason is that the compiler already transformed the 1 into "1" for the `echo` statement. However, a term rewriting macro should not change the @@ -1490,27 +1491,24 @@ semantics anyway. In fact, they can be deactivated with the `--patterns:off`:opt command line option or temporarily with the `patterns` pragma. -The `{}` operator -~~~~~~~~~~~~~~~~~~~ +### The `{}` operator A pattern expression can be bound to a pattern parameter via the `expr{param}` notation: -.. code-block:: nim - + ```nim template t{(0|1|2){x}}(x: untyped): untyped = x + 1 let a = 1 # outputs 2: echo a + ``` -The `~` operator -~~~~~~~~~~~~~~~~~~ +### The `~` operator The `~` operator is the 'not' operator in patterns: -.. code-block:: nim - + ```nim template t{x = (~x){y} and (~x){z}}(x, y, z: bool) = x = y if x: x = z @@ -1521,16 +1519,15 @@ The `~` operator is the 'not' operator in patterns: c = false a = b and c echo a + ``` -The `*` operator -~~~~~~~~~~~~~~~~~~ +### The `*` operator The `*` operator can *flatten* a nested binary expression like `a & b & c` to `&(a, b, c)`: -.. code-block:: nim - + ```nim var calls = 0 @@ -1546,6 +1543,7 @@ to `&(a, b, c)`: # check that it's been optimized properly: doAssert calls == 1 + ``` The second operator of `*` must be a parameter; it is used to gather all the @@ -1554,19 +1552,17 @@ is passed to `optConc` in `a` as a special list (of kind `nkArgList`) which is flattened into a call expression; thus the invocation of `optConc` produces: -.. code-block:: nim - - `&&`("my", space & "awe", "some ", "concat") + ```nim + `&&`("my", space & "awe", "some ", "concat") + ``` -The `**` operator -~~~~~~~~~~~~~~~~~~~ +### The `**` operator The `**` is much like the `*` operator, except that it gathers not only all the arguments, but also the matched operators in reverse polish notation: -.. code-block:: nim - + ```nim import std/macros type @@ -1587,6 +1583,7 @@ all the arguments, but also the matched operators in reverse polish notation: var x, y, z: Matrix echo x + y * z - x + ``` This passes the expression `x + y * z - x` to the `optM` macro as an `nnkArgList` node containing:: @@ -1610,13 +1607,13 @@ Parameters in a pattern are type checked in the matching process. If a parameter is of the type `varargs`, it is treated specially and can match 0 or more arguments in the AST to be matched against: -.. code-block:: nim - + ```nim template optWrite{ write(f, x) ((write|writeLine){w})(f, y) }(x, y: varargs[untyped], f: File, w: untyped) = w(f, x, y) + ``` noRewrite pragma @@ -1630,12 +1627,12 @@ e.g. when rewriting term to same term plus extra content. `noRewrite` pragma can actually prevent further rewriting on marked code, e.g. with given example `echo("ab")` will be rewritten just once: -.. code-block:: nim - + ```nim template pwnEcho{echo(x)}(x: untyped) = {.noRewrite.}: echo("pwned!") echo "ab" + ``` `noRewrite` pragma can be useful to control term-rewriting macros recursion. @@ -1647,13 +1644,13 @@ Example: Partial evaluation The following example shows how some simple partial evaluation can be implemented with term rewriting: -.. code-block:: nim - + ```nim proc p(x, y: int; cond: bool): int = result = if cond: x + y else: x - y template optP1{p(x, y, true)}(x, y: untyped): untyped = x + y template optP2{p(x, y, false)}(x, y: untyped): untyped = x - y + ``` Example: Hoisting @@ -1661,8 +1658,7 @@ Example: Hoisting The following example shows how some form of hoisting can be implemented: -.. code-block:: nim - + ```nim import std/pegs template optPeg{peg(pattern)}(pattern: string{lit}): Peg = @@ -1672,6 +1668,7 @@ The following example shows how some form of hoisting can be implemented: for i in 0 .. 3: echo match("(a b c)", peg"'(' @ ')'") echo match("W_HI_Le", peg"\y 'while'") + ``` The `optPeg` template optimizes the case of a peg constructor with a string literal, so that the pattern will only be parsed once at program startup and @@ -1685,8 +1682,7 @@ AST based overloading Parameter constraints can also be used for ordinary routine parameters; these constraints then affect ordinary overloading resolution: -.. code-block:: nim - + ```nim proc optLit(a: string{lit|`const`}) = echo "string literal" proc optLit(a: string) = @@ -1701,6 +1697,7 @@ constraints then affect ordinary overloading resolution: optLit("literal") optLit(constant) optLit(variable) + ``` However, the constraints `alias` and `noalias` are not available in ordinary routines. @@ -1715,7 +1712,7 @@ Nim has two flavors of parallelism: Nim has a builtin thread pool that can be used for CPU intensive tasks. For IO intensive tasks the `async` and `await` features should be -used instead. Both parallel and spawn need the `threadpool `_ +used instead. Both parallel and spawn need the [threadpool](threadpool.html) module to work. Somewhat confusingly, `spawn` is also used in the `parallel` statement @@ -1740,8 +1737,7 @@ Spawn statement The `spawn`:idx: statement can be used to pass a task to the thread pool: -.. code-block:: nim - + ```nim import std/threadpool proc processLine(line: string) = @@ -1750,6 +1746,7 @@ The `spawn`:idx: statement can be used to pass a task to the thread pool: for x in lines("myinput.txt"): spawn processLine(x) sync() + ``` For reasons of type safety and implementation simplicity the expression that `spawn` takes is restricted: @@ -1773,8 +1770,7 @@ a `data flow variable`:idx: `FlowVar[T]` that can be read from. The reading with the `^` operator is **blocking**. However, one can use `blockUntilAny` to wait on multiple flow variables at the same time: -.. code-block:: nim - + ```nim import std/threadpool, ... # wait until 2 out of 3 servers received the update: @@ -1786,6 +1782,7 @@ wait on multiple flow variables at the same time: assert index >= 0 responses.del(index) discard blockUntilAny(responses) + ``` Data flow variables ensure that no data races are possible. Due to technical limitations, not every type `T` can be used in @@ -1800,9 +1797,7 @@ Parallel statement Example: -.. code-block:: nim - :test: "nim c --threads:on $1" - + ```nim test = "nim c --threads:on $1" # Compute pi in an inefficient way import std/[strutils, math, threadpool] {.experimental: "parallel".} @@ -1818,6 +1813,7 @@ Example: result += ch[k] echo formatFloat(pi(5000)) + ``` The parallel statement is the preferred mechanism to introduce parallelism in a @@ -1858,8 +1854,7 @@ lock of level `N < M`. Another lock of level `M` cannot be acquired. Locks of the same level can only be acquired *at the same time* within a single `locks` section: -.. code-block:: nim - + ```nim var a, b: TLock[2] var x: TLock[1] # invalid locking order: TLock[1] cannot be acquired before TLock[2]: @@ -1879,14 +1874,14 @@ single `locks` section: # valid locking order, locks of the same level acquired at the same time: {.locks: [a, b].}: ... + ``` Here is how a typical multilock statement can be implemented in Nim. Note how the runtime check is required to ensure a global ordering for two locks `a` and `b` of the same lock level: -.. code-block:: nim - + ```nim template multilock(a, b: ptr TLock; body: untyped) = if cast[ByteAddress](a) < cast[ByteAddress](b): pthread_mutex_lock(a) @@ -1900,20 +1895,21 @@ and `b` of the same lock level: finally: pthread_mutex_unlock(a) pthread_mutex_unlock(b) + ``` Whole routines can also be annotated with a `locks` pragma that takes a lock level. This then means that the routine may acquire locks of up to this level. This is essential so that procs can be called within a `locks` section: -.. code-block:: nim - + ```nim proc p() {.locks: 3.} = discard var a: TLock[4] {.locks: [a].}: # p's locklevel (3) is strictly less than a's (4) so the call is allowed: p() + ``` As usual, `locks` is an inferred effect and there is a subtype @@ -1929,8 +1925,7 @@ cannot be inferred statically, leading to compiler warnings. By using `{.locks: "unknown".}`, the base method can be marked explicitly as having unknown lock level as well: -.. code-block:: nim - + ```nim type SomeBase* = ref object of RootObj type SomeDerived* = ref object of SomeBase memberProc*: proc () @@ -1939,5 +1934,6 @@ having unknown lock level as well: method testMethod(g: SomeDerived) = if g.memberProc != nil: g.memberProc() + ``` This feature may be removed in the future due to its practical difficulties. diff --git a/doc/manual_experimental_strictnotnil.rst b/doc/manual_experimental_strictnotnil.md similarity index 85% rename from doc/manual_experimental_strictnotnil.rst rename to doc/manual_experimental_strictnotnil.md index b14e5f9f38e6..b6d8e796e29a 100644 --- a/doc/manual_experimental_strictnotnil.rst +++ b/doc/manual_experimental_strictnotnil.md @@ -6,13 +6,15 @@ Strict not nil checking **Note:** This feature is experimental, you need to enable it with -.. code-block:: nim + ```nim {.experimental: "strictNotNil".} + ``` or -.. code-block:: cmd + ```cmd nim c --experimental:strictNotNil + ``` In the second case it would check builtin and imported modules as well. @@ -39,7 +41,7 @@ not nil You can annotate a type where nil isn't a valid value with `not nil`. -.. code-block:: nim + ```nim type NilableObject = ref object a: int @@ -57,11 +59,12 @@ You can annotate a type where nil isn't a valid value with `not nil`. p(x) else: p(x) # ok + ``` If a type can include `nil` as a valid value, dereferencing values of the type -is checked by the compiler: if a value which might be nil is derefenced, this +is checked by the compiler: if a value which might be nil is dereferenced, this produces a warning by default, you can turn this into an error using the compiler options `--warningAsError:strictNotNil`:option:. @@ -76,7 +79,7 @@ Note: test that/TODO for code/manual. nilability state ----------------- -Currently a nilable value can be `Safe`, `MaybeNil` or `Nil` : we use internally `Parent` and `Unreachable` but this is an implementation detail(a parent layer has the actual nilability). +Currently, a nilable value can be `Safe`, `MaybeNil` or `Nil` : we use internally `Parent` and `Unreachable` but this is an implementation detail(a parent layer has the actual nilability). - `Safe` means it shouldn't be nil at that point: e.g. after assignment to a non-nil value or `not a.isNil` check @@ -87,7 +90,7 @@ Currently a nilable value can be `Safe`, `MaybeNil` or `Nil` : we use internally - `Unreachable` means it shouldn't be possible to access this in this branch: so we do generate a warning as well. -We show an error for each dereference (`[]`, `.field`, `[index]` `()` etc) which is of a tracked expression which is +We show an error for each dereference (`[]`, `.field`, `[index]` `()` etc.) which is of a tracked expression which is in `MaybeNil` or `Nil` state. @@ -118,36 +121,39 @@ call args rules When we call with arguments, we have two cases when we might change the nilability. -.. code-block:: nim + ```nim callByVar(a) + ``` Here `callByVar` can re-assign `a`, so this might change `a`'s nilability, so we change it to `MaybeNil`. This is also a possible aliasing `move out` (moving out of a current alias set). -.. code-block:: nim + ```nim call(a) + ``` -Here `call` can change a field or element of `a`, so if we have a dependant expression of `a` : e.g. `a.field`. Dependats become `MaybeNil`. +Here `call` can change a field or element of `a`, so if we have a dependant expression of `a` : e.g. `a.field`. Dependants become `MaybeNil`. branches rules --------------- Branches are the reason we do nil checking like this: with flow checking. -Sources of brancing are `if`, `while`, `for`, `and`, `or`, `case`, `try` and combinations with `return`, `break`, `continue` and `raise` +Sources of branching are `if`, `while`, `for`, `and`, `or`, `case`, `try` and combinations with `return`, `break`, `continue` and `raise` We create a new layer/"scope" for each branch where we map expressions to nilability. This happens when we "fork": usually on the beginning of a construct. When branches "join" we usually unify their expression maps or/and nilabilities. Merging usually merges maps and alias sets: nilabilities are merged like this: -.. code-block:: nim + ```nim template union(l: Nilability, r: Nilability): Nilability = ## unify two states if l == r: l else: MaybeNil + ``` Special handling is for `.isNil` and `== nil`, also for `not`, `and` and `or`. @@ -164,7 +170,7 @@ We want to track also field(dot) and index(bracket) expressions. We track some of those compound expressions which might be nilable as dependants of their bases: `a.field` is changed if `a` is moved (re-assigned), similarly `a[index]` is dependent on `a` and `a.field.field` on `a.field`. -When we move the base, we update dependants to `MaybeNil`. Otherwise we usually start with type nilability. +When we move the base, we update dependants to `MaybeNil`. Otherwise, we usually start with type nilability. When we call args, we update the nilability of their dependants to `MaybeNil` as the calls usually can change them. We might need to check for `strictFuncs` pure funcs and not do that then. @@ -172,10 +178,10 @@ We might need to check for `strictFuncs` pure funcs and not do that then. For field expressions `a.field`, we calculate an integer value based on a hash of the tree and just accept equivalent trees as equivalent expressions. For item expression `a[index]`, we also calculate an integer value based on a hash of the tree and accept equivalent trees as equivalent expressions: for static values only. -For now we support only constant indices: we dont track expression with no-const indices. For those we just report a warning even if they are safe for now: one can use a local variable to workaround. For loops this might be annoying: so one should be able to turn off locally the warning using the `{.warning[StrictCheckNotNil]:off}.`. +For now, we support only constant indices: we don't track expression with no-const indices. For those we just report a warning even if they are safe for now: one can use a local variable to workaround. For loops this might be annoying: so one should be able to turn off locally the warning using the `{.warning[StrictNotNil]:off.}`. For bracket expressions, in the future we might count `a[]` as the same general expression. -This means we should should the index but otherwise handle it the same for assign (maybe "aliasing" all the non-static elements) and differentiate only for static: e.g. `a[0]` and `a[1]`. +This means we should the index but otherwise handle it the same for assign (maybe "aliasing" all the non-static elements) and differentiate only for static: e.g. `a[0]` and `a[1]`. element tracking ----------------- @@ -183,8 +189,9 @@ element tracking When we assign an object construction, we should track the fields as well: -.. code-block:: nim + ```nim var a = Nilable(field: Nilable()) # a : Safe, a.field: Safe + ``` Usually we just track the result of an expression: probably this should apply for elements in other cases as well. Also related to tracking initialization of expressions/fields. @@ -193,14 +200,15 @@ unstructured control flow rules ------------------------------- Unstructured control flow keywords as `return`, `break`, `continue`, `raise` mean that we jump from a branch out. -This means that if there is code after the finishing of the branch, it would be ran if one hasn't hit the direct parent branch of those: so it is similar to an `else`. In those cases we should use the reverse nilabilities for the local to the condition expressions. E.g. +This means that if there is code after the finishing of the branch, it would be run if one hasn't hit the direct parent branch of those: so it is similar to an `else`. In those cases we should use the reverse nilabilities for the local to the condition expressions. E.g. -.. code-block:: nim + ```nim for a in c: if not a.isNil: b() break code # here a: Nil , because if not, we would have breaked + ``` aliasing @@ -214,17 +222,19 @@ Assignments and other changes to nilability can move / move out expressions of s `move`: Moving `left` to `right` means we remove `left` from its current set and unify it with the `right`'s set. This means it stops being aliased with its previous aliases. -.. code-block:: nim + ```nim var left = b left = right # moving left to right + ``` `move out`: Moving out `left` might remove it from the current set and ensure that it's in its own set as a single element. e.g. -.. code-block:: nim + ```nim var left = b left = nil # moving out + ``` initialization of non nilable and nilable values @@ -235,7 +245,7 @@ TODO warnings and errors --------------------- -We show an error for each dereference (`[]`, `.field`, `[index]` `()` etc) which is of a tracked expression which is +We show an error for each dereference (`[]`, `.field`, `[index]` `()` etc.) which is of a tracked expression which is in `MaybeNil` or `Nil` state. We might also show a history of the transitions and the reasons for them that might change the nilability of the expression. diff --git a/doc/markdown_rst.md b/doc/markdown_rst.md new file mode 100644 index 000000000000..c507f25117de --- /dev/null +++ b/doc/markdown_rst.md @@ -0,0 +1,258 @@ +========================================== +Nim-flavored Markdown and reStructuredText +========================================== + +:Author: Andrey Makarov +:Version: |nimversion| + +.. default-role:: code +.. include:: rstcommon.rst +.. contents:: + +Both `Markdown`:idx: (md) and `reStructuredText`:idx: (RST) are markup +languages whose goal is to typeset texts with complex structure, +formatting and references using simple plaintext representation. + +Command line usage +================== + +Usage (to convert Markdown into HTML): + + ```cmd + nim md2html markdown_rst.md + ``` + +Output:: + You're reading it! + +The `md2tex`:option: command is invoked identically to `md2html`:option:, +but outputs a ``.tex`` file instead of ``.html``. + +These tools embedded into Nim compiler; the compiler can output +the result to HTML \[#html] or Latex \[#latex]. + +\[#html] commands `nim doc`:cmd: for ``*.nim`` files and + `nim rst2html`:cmd: for ``*.rst`` files + +\[#latex] commands `nim doc2tex`:cmd: for ``*.nim`` and + `nim rst2tex`:cmd: for ``*.rst``. + +Full list of supported commands: + +=================== ====================== ============ ============== +command runs on... input format output format +=================== ====================== ============ ============== +`nim md2html`:cmd: standalone md files ``.md`` ``.html`` HTML +`nim md2tex`:cmd: same same ``.tex`` LaTeX +`nim rst2html`:cmd: standalone rst files ``.rst`` ``.html`` HTML +`nim rst2tex`:cmd: same same ``.tex`` LaTeX +`nim doc`:cmd: documentation comments ``.nim`` ``.html`` HTML +`nim doc2tex`:cmd: same same ``.tex`` LaTeX +`nim jsondoc`:cmd: same same ``.json`` JSON +=================== ====================== ============ ============== + + +Basic markup +============ + +If you are new to Markdown/RST please consider reading the following: + +1) [Markdown Basic Syntax] +2) a long specification of Markdown: [CommonMark Spec] +3) a short [quick introduction] to RST +4) an [RST reference]: a comprehensive cheatsheet for RST +5) a more formal 50-page [RST specification]. + +Features +-------- + +A large subset is implemented with some [limitations] and +[additional Nim-specific features]. + +Supported standard RST features: + +* body elements + + sections + + transitions + + paragraphs + + bullet lists using \+, \*, \- + + enumerated lists using arabic numerals or alphabet + characters: 1. ... 2. ... *or* a. ... b. ... *or* A. ... B. ... + + footnotes (including manually numbered, auto-numbered, auto-numbered + with label, and auto-symbol footnotes) and citations + + definition lists + + field lists + + option lists + + indented literal blocks + + quoted literal blocks + + line blocks + + simple tables + + directives (see official documentation in [RST directives list]): + - ``image``, ``figure`` for including images and videos + - ``code`` + - ``contents`` (table of contents), ``container``, ``raw`` + - ``include`` + - admonitions: "attention", "caution", "danger", "error", "hint", + "important", "note", "tip", "warning", "admonition" + - substitution definitions: `replace` and `image` + + comments +* inline markup + + *emphasis*, **strong emphasis**, + ``inline literals``, hyperlink references (including embedded URI), + substitution references, standalone hyperlinks, + internal links (inline and outline) + + \`interpreted text\` with roles ``:literal:``, ``:strong:``, + ``emphasis``, ``:sub:``/``:subscript:``, ``:sup:``/``:superscript:`` + (see [RST roles list] for description). + + inline internal targets + +Additional Nim-specific features +-------------------------------- + +* directives: ``code-block`` \[cmp:Sphinx], ``title``, + ``index`` \[cmp:Sphinx] +* predefined roles + - ``:nim:`` (default), ``:c:`` (C programming language), + ``:python:``, ``:yaml:``, ``:java:``, ``:cpp:`` (C++), ``:csharp`` (C#). + That is every language that [highlite](highlite.html) supports. + They turn on appropriate syntax highlighting in inline code. + + .. Note:: default role for Nim files is ``:nim:``, + for ``*.rst`` it's currently ``:literal:``. + + - generic command line highlighting roles: + - ``:cmd:`` for commands and common shells syntax + - ``:console:`` the same for interactive sessions + (commands should be prepended by ``$``) + - ``:program:`` for executable names \[cmp:Sphinx] + (one can just use ``:cmd:`` on single word) + - ``:option:`` for command line options \[cmp:Sphinx] + - ``:tok:``, a role for highlighting of programming language tokens +* ***triple emphasis*** (bold and italic) using \*\*\* +* ``:idx:`` role for \`interpreted text\` to include the link to this + text into an index (example: [Nim index]). +* double slash `//` in option lists serves as a prefix for any option that + starts from a word (without any leading symbols like `-`, `--`, `/`):: + + //compile compile the project + //doc generate documentation + + Here the dummy `//` will disappear, while options `compile`:option: + and `doc`:option: will be left in the final document. + +\[cmp:Sphinx] similar but different from the directives of + Python [Sphinx directives] and [Sphinx roles] extensions + +Extra features +-------------- + +Optional additional features, by default turned on: + +* emoji / smiley symbols +* Markdown tables +* Markdown code blocks. For them the same additional arguments as for RST + code blocks can be provided (e.g. `test` or `number-lines`) but with + a one-line syntax like this:: + + ```nim test number-lines=10 + echo "ok" + ``` +* Markdown links +* Markdown headlines +* Markdown block quotes +* using ``1`` as auto-enumerator in enumerated lists like RST ``#`` + (auto-enumerator ``1`` can not be used with ``#`` in the same list) + +.. Note:: By default Nim has ``roSupportMarkdown`` and + ``roSupportRawDirective`` turned **on**. + +.. warning:: Using Nim-specific features can cause other RST implementations + to fail on your document. + +Idiosyncrasies +-------------- + +Currently we do **not** aim at 100% Markdown or RST compatibility in inline +markup recognition rules because that would provide very little user value. +This parser has 2 modes for inline markup: + +1) Markdown-like mode which is enabled by `roPreferMarkdown` option + (turned **on** by default). + + .. Note:: RST features like directives are still turned **on** + +2) Compatibility mode which is RST rules. + +.. Note:: in both modes the parser interpretes text between single + backticks (code) identically: + backslash does not escape; the only exception: ``\`` folowed by ` + does escape so that we can always input a single backtick ` in + inline code. However that makes impossible to input code with + ``\`` at the end in *single* backticks, one must use *double* + backticks:: + + `\` -- WRONG + ``\`` -- GOOD + So single backticks can always be input: `\`` will turn to ` code + +.. Attention:: + We don't support some obviously poor design choices of Markdown (or RST). + + - no support for the rule of 2 spaces causing a line break in Markdown + (use RST "line blocks" syntax for making line breaks) + + - interpretation of Markdown block quotes is also slightly different, + e.g. case + + :: + + >>> foo + > bar + >>baz + + is a single 3rd-level quote `foo bar baz` in original Markdown, while + in Nim we naturally see it as 3rd-level quote `foo` + 1st level `bar` + + 2nd level `baz`: + + >>> foo + > bar + >>baz + +Limitations +----------- + +* no Unicode support in character width calculations +* body elements + - no roman numerals in enumerated lists + - no doctest blocks + - no grid tables + - some directives are missing (check official [RST directives list]): + ``parsed-literal``, ``sidebar``, ``topic``, ``math``, ``rubric``, + ``epigraph``, ``highlights``, ``pull-quote``, ``compound``, + ``table``, ``csv-table``, ``list-table``, ``section-numbering``, + ``header``, ``footer``, ``meta``, ``class`` + - no ``role`` directives and no custom interpreted text roles + - some standard roles are not supported (check [RST roles list]) + - no generic admonition support +* inline markup + - no simple-inline-markup + - no embedded aliases + +Additional resources +-------------------- + +* See [Nim DocGen Tools Guide](docgen.html) for the details about + `nim doc`:cmd: command and idiosyncrasies of documentation markup in + ``.nim`` files and Nim programming language projects. +* See also documentation for [rst module](rst.html) -- Nim RST/Markdown parser. + +.. _Markdown Basic Syntax: https://docs.github.com/en/get-started/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax +.. _CommonMark Spec: https://spec.commonmark.org/0.30 +.. _quick introduction: https://docutils.sourceforge.io/docs/user/rst/quickstart.html +.. _RST reference: https://docutils.sourceforge.io/docs/user/rst/quickref.html +.. _RST specification: https://docutils.sourceforge.io/docs/ref/rst/restructuredtext.html +.. _RST directives list: https://docutils.sourceforge.io/docs/ref/rst/directives.html +.. _RST roles list: https://docutils.sourceforge.io/docs/ref/rst/roles.html +.. _Nim index: https://nim-lang.org/docs/theindex.html +.. _Sphinx directives: https://www.sphinx-doc.org/en/master/usage/restructuredtext/directives.html +.. _Sphinx roles: https://www.sphinx-doc.org/en/master/usage/restructuredtext/roles.html diff --git a/doc/mm.rst b/doc/mm.md similarity index 78% rename from doc/mm.rst rename to doc/mm.md index b6941a901cde..9d1fc1955988 100644 --- a/doc/mm.rst +++ b/doc/mm.md @@ -11,7 +11,7 @@ Nim's Memory Management .. - "The road to hell is paved with good intentions." +> "The road to hell is paved with good intentions." Multi-paradigm Memory Management Strategies @@ -28,14 +28,14 @@ To choose the memory management strategy use the `--mm:` switch. ARC/ORC ------- -`--mm:orc` is a memory management mode primarily based on reference counting. Cycles -in the object graph are handled by a "cycle collector" which is based on "trial deletion". -Since algorithms based on "tracing" are not used, the runtime behavior is oblivious to -the involved heap sizes. +ORC is the default memory management strategy. It is a memory +management mode primarily based on reference counting. Reference cycles are +handled by a cycle collection mechanism based on "trial deletion". +Since algorithms based on "tracing" are not used, the runtime behavior is oblivious to the involved heap and stack sizes. The reference counting operations (= "RC ops") do not use atomic instructions and do not have to -- instead entire subgraphs are *moved* between threads. The Nim compiler also aggressively -optimizes away RC ops and exploits `move semantics `_. +optimizes away RC ops and exploits [move semantics](destructors.html#move-semantics). Nim performs a fair share of optimizations for ARC/ORC; you can inspect what it did to your time critical function via `--expandArc:functionName`. @@ -57,12 +57,11 @@ and leaks memory with `--mm:arc`, in other words, for `async` you need to use `- Other MM modes -------------- -.. note:: The default `refc` GC is incremental, thread-local and not "stop-the-world". +.. note:: The `refc` GC is incremental, thread-local and not "stop-the-world". ---mm:refc This is the default memory management strategy. It's a - deferred reference counting based garbage collector - with a simple Mark&Sweep backup GC in order to collect cycles. Heaps are thread-local. - `This document `_ contains further information. +--mm:refc It's a deferred reference counting based garbage collector + with a simple Mark&Sweep backup GC in order to collect cycles. + Heaps are thread-local. [This document](refc.html) contains further information. --mm:markAndSweep Simple Mark-And-Sweep based garbage collector. Heaps are thread-local. --mm:boehm Boehm based garbage collector, it offers a shared heap. @@ -89,7 +88,7 @@ None Manual Manual Manual `--mm:none` .. default-role:: code .. include:: rstcommon.rst -JavaScript's garbage collector is used for the `JavaScript and NodeJS -`_ compilation targets. -The `NimScript `_ target uses the memory management strategy built into +JavaScript's garbage collector is used for the [JavaScript and NodeJS]( +backends.html#backends-the-javascript-target) compilation targets. +The [NimScript](nims.html) target uses the memory management strategy built into the Nim compiler. diff --git a/doc/nep1.rst b/doc/nep1.md similarity index 95% rename from doc/nep1.rst rename to doc/nep1.md index 8eb31332f8f4..0c62e3f9c606 100644 --- a/doc/nep1.rst +++ b/doc/nep1.md @@ -21,7 +21,7 @@ library should follow. Note that there can be exceptions to these rules. Nim being as flexible as it is, there will be parts of this style guide that don't make sense in certain contexts. Furthermore, just as -`Python's style guide`_ changes +[Python's style guide](http://legacy.python.org/dev/peps/pep-0008/) changes over time, this style guide will too. These rules will only be enforced for contributions to the Nim @@ -49,7 +49,7 @@ Spacing and Whitespace Conventions Not all editors support automatic alignment of code sections, and re-aligning long sections of code by hand can quickly become tedious. - .. code-block:: nim + ```nim # This is bad, as the next time someone comes # to edit this code block, they # must re-align all the assignments again: @@ -60,6 +60,7 @@ Spacing and Whitespace Conventions CalId* = int LongLong* = int64 LongLongPtr* = ptr LongLong + ``` Naming Conventions @@ -69,7 +70,7 @@ Naming Conventions camelCase with the exception of constants which **may** use PascalCase but are not required to. - .. code-block:: nim + ```nim # Constants can start with either a lower case or upper case letter. const aConstant = 42 const FooBar = 4.2 @@ -79,6 +80,7 @@ Naming Conventions # Types must start with an uppercase letter. type FooBar = object + ``` For constants coming from a C/C++ wrapper, ALL_UPPERCASE are allowed, but ugly. (Why shout CONSTANT? Constants do no harm, variables do!) @@ -89,41 +91,45 @@ Naming Conventions that will be used the most, add the suffixes to the pointer variants only. The same applies to C/C++ wrappers. - .. code-block:: nim + ```nim type Handle = object # Will be used most often fd: int64 HandleRef = ref Handle # Will be used less often + ``` - Exception and Error types should have the "Error" or "Defect" suffix. - .. code-block:: nim + ```nim type ValueError = object of CatchableError AssertionDefect = object of Defect Foo = object of Exception # bad style, try to inherit CatchableError or Defect + ``` - Unless marked with the `{.pure.}` pragma, members of enums should have an identifying prefix, such as an abbreviation of the enum's name. - .. code-block:: nim + ```nim type PathComponent = enum pcDir pcLinkToDir pcFile pcLinkToFile + ``` - Non-pure enum values should use camelCase whereas pure enum values should use PascalCase. - .. code-block:: nim + ```nim type PathComponent {.pure.} = enum Dir LinkToDir File LinkToFile + ``` - In the age of HTTP, HTML, FTP, TCP, IP, UTF, WWW it is foolish to pretend these are somewhat special words requiring all uppercase. Instead treat them @@ -153,9 +159,9 @@ The library uses a simple naming scheme that makes use of common abbreviations to keep the names short but meaningful. -------------------- ------------ -------------------------------------- +=================== ============ ====================================== English word To use Notes -------------------- ------------ -------------------------------------- +=================== ============ ====================================== initialize initFoo initializes a value type `Foo` new newFoo initializes a reference type `Foo` via `new` or a value type `Foo` @@ -220,7 +226,7 @@ literal lit string str identifier ident indentation indent -------------------- ------------ -------------------------------------- +=================== ============ ====================================== Coding Conventions @@ -230,12 +236,13 @@ Coding Conventions are required. Use a procedure's implicit `result` variable whenever possible. This improves readability. - .. code-block:: nim + ```nim proc repeat(text: string, x: int): string = result = "" for i in 0..x: result.add($i) + ``` - Use a proc when possible, only using the more powerful facilities of macros, templates, iterators, and converters when necessary. @@ -252,15 +259,16 @@ Conventions for multi-line statements and expressions - Tuples which are longer than one line should indent their parameters to align with the parameters above it. - .. code-block:: nim + ```nim type LongTupleA = tuple[wordyTupleMemberOne: int, wordyTupleMemberTwo: string, wordyTupleMemberThree: float] + ``` - Similarly, any procedure and procedure type declarations that are longer than one line should do the same thing. - .. code-block:: nim + ```nim type EventCallback = proc (timeReceived: Time, errorCode: int, event: Event, output: var string) @@ -268,13 +276,15 @@ Conventions for multi-line statements and expressions proc lotsOfArguments(argOne: string, argTwo: int, argThree: float, argFour: proc(), argFive: bool): int {.heyLookALongPragma.} = + ``` - Multi-line procedure calls should continue on the same column as the opening parenthesis (like multi-line procedure declarations). - .. code-block:: nim + ```nim startProcess(nimExecutable, currentDirectory, compilerArguments environment, processOptions) + ``` Miscellaneous ------------- @@ -290,18 +300,20 @@ Miscellaneous use this: - .. code-block:: nim + ```nim let a = """ foo bar """ + ``` instead of: - .. code-block:: nim + ```nim let a = """foo bar """ + ``` - A getter API for a private field `foo` should preferably be named `foo`, not `getFoo`. A getter-like API should preferably be named `getFoo`, not `foo` if: diff --git a/doc/nimc.rst b/doc/nimc.md similarity index 92% rename from doc/nimc.rst rename to doc/nimc.md index aa665049142a..1b3318ee6090 100644 --- a/doc/nimc.rst +++ b/doc/nimc.md @@ -11,9 +11,9 @@ .. - "Look at you, hacker. A pathetic creature of meat and bone, panting and - sweating as you run through my corridors. How can you challenge a perfect, - immortal machine?" +> "Look at you, hacker. A pathetic creature of meat and bone, panting and +> sweating as you run through my corridors. How can you challenge a perfect, +> immortal machine?" Introduction @@ -21,10 +21,10 @@ Introduction This document describes the usage of the *Nim compiler* on the different supported platforms. It is not a definition of the Nim -programming language (which is covered in the `manual `_). +programming language (which is covered in the [manual](manual.html)). Nim is free software; it is licensed under the -`MIT License `_. +[MIT License](http://www.opensource.org/licenses/mit-license.php). Compiler Usage @@ -130,13 +130,13 @@ Level Description ===== ============================================ 0 Minimal output level for the compiler. 1 Displays compilation of all the compiled files, including those imported - by other modules or through the `compile pragma - `_. + by other modules or through the [compile pragma]( + manual.html#implementation-specific-pragmas-compile-pragma). This is the default level. 2 Displays compilation statistics, enumerates the dynamic libraries that will be loaded by the final binary, and dumps to - standard output the result of applying `a filter to the source code - `_ if any filter was used during compilation. + standard output the result of applying [a filter to the source code]( + filters.html) if any filter was used during compilation. 3 In addition to the previous levels dumps a debug stack trace for compiler developers. ===== ============================================ @@ -147,16 +147,16 @@ Compile-time symbols Through the `-d:x`:option: or `--define:x`:option: switch you can define compile-time symbols for conditional compilation. The defined switches can be checked in -source code with the `when statement -`_ and -`defined proc `_. The typical use of this switch is +source code with the [when statement]( +manual.html#statements-and-expressions-when-statement) and +[defined proc](system.html#defined,untyped). The typical use of this switch is to enable builds in release mode (`-d:release`:option:) where optimizations are enabled for better performance. Another common use is the `-d:ssl`:option: switch to activate SSL sockets. Additionally, you may pass a value along with the symbol: `-d:x=y`:option: -which may be used in conjunction with the `compile-time define -pragmas`_ +which may be used in conjunction with the [compile-time define +pragmas](manual.html#implementation-specific-pragmas-compileminustime-define-pragmas) to override symbols during build time. Compile-time symbols are completely **case insensitive** and underscores are @@ -219,15 +219,15 @@ Command-line settings have priority over configuration file settings. The default build of a project is a `debug build`:idx:. To compile a `release build`:idx: define the `release` symbol: -.. code:: cmd - + ```cmd nim c -d:release myproject.nim + ``` To compile a `dangerous release build`:idx: define the `danger` symbol: -.. code:: cmd - + ```cmd nim c -d:danger myproject.nim + ``` Search path handling @@ -268,7 +268,7 @@ The `_r` suffix is used for release builds, `_d` is for debug builds. This makes it easy to delete all generated files. The `--nimcache`:option: -`compiler switch <#compiler-usage-commandminusline-switches>`_ can be used to +[compiler switch][command-line switches] can be used to to change the ``nimcache`` directory. However, the generated C code is not platform-independent. C code generated for @@ -281,9 +281,9 @@ Compiler Selection To change the compiler from the default compiler (at the command line): -.. code:: cmd - + ```cmd nim c --cc:llvm_gcc --compile_only myfile.nim + ``` This uses the configuration defined in ``config\nim.cfg`` for `llvm_gcc`:cmd:. @@ -303,18 +303,18 @@ Cross-compilation To cross compile, use for example: -.. code:: cmd - + ```cmd nim c --cpu:i386 --os:linux --compileOnly --genScript myproject.nim + ``` Then move the C code and the compile script `compile_myproject.sh`:cmd: to your Linux i386 machine and run the script. Another way is to make Nim invoke a cross compiler toolchain: -.. code:: cmd - + ```cmd nim c --cpu:arm --os:linux myproject.nim + ``` For cross compilation, the compiler invokes a C compiler named like `$cpu.$os.$cc` (for example arm.linux.gcc) and the configuration @@ -330,22 +330,22 @@ Cross-compilation for Windows To cross-compile for Windows from Linux or macOS using the MinGW-w64 toolchain: -.. code:: cmd - + ```cmd nim c -d:mingw myproject.nim # `nim r` also works, running the binary via `wine` or `wine64`: nim r -d:mingw --eval:'import os; echo "a" / "b"' + ``` Use `--cpu:i386`:option: or `--cpu:amd64`:option: to switch the CPU architecture. The MinGW-w64 toolchain can be installed as follows: -.. code:: cmd - + ```cmd apt install mingw-w64 # Ubuntu yum install mingw32-gcc yum install mingw64-gcc # CentOS - requires EPEL brew install mingw-w64 # OSX + ``` Cross-compilation for Android @@ -355,7 +355,7 @@ There are two ways to compile for Android: terminal programs (Termux) and with the NDK (Android Native Development Kit). The first one is to treat Android as a simple Linux and use -`Termux `_ to connect and run the Nim compiler +[Termux](https://wiki.termux.com) to connect and run the Nim compiler directly on android as if it was Linux. These programs are console-only programs that can't be distributed in the Play Store. @@ -363,8 +363,8 @@ Use regular `nim c`:cmd: inside termux to make Android terminal programs. Normal Android apps are written in Java, to use Nim inside an Android app you need a small Java stub that calls out to a native library written in -Nim using the `NDK `_. You can also use -`native-activity `_ +Nim using the [NDK](https://developer.android.com/ndk). You can also use +[native-activity](https://developer.android.com/ndk/samples/sample_na) to have the Java stub be auto-generated for you. Use `nim c -c --cpu:arm --os:android -d:androidNDK --noMain:on`:cmd: to @@ -380,11 +380,11 @@ stuff is done, it's very important to call `NimMain()`:c: in order to initialize Nim's garbage collector and to run the top level statements of your program. -.. code-block:: Nim - + ```Nim proc NimMain() {.importc.} proc glfmMain*(display: ptr GLFMDisplay) {.exportc.} = NimMain() # initialize garbage collector memory, types and stack + ``` The name `NimMain` can be influenced via the `--nimMainPrefix:prefix` switch. @@ -410,11 +410,11 @@ the iOS setup is done, it's very important to call `NimMain()`:c: to initialize Nim's garbage collector and to run the top-level statements of your program. -.. code-block:: Nim - + ```Nim proc NimMain() {.importc.} proc glfmMain*(display: ptr GLFMDisplay) {.exportc.} = NimMain() # initialize garbage collector memory, types and stack + ``` Note: XCode's "make clean" gets confused about the generated nim.c files, so you need to clean those files manually to do a clean build. @@ -430,9 +430,10 @@ Simply add `--os:nintendoswitch`:option: to your usual `nim c`:cmd: or `nim cpp`:cmd: command and set the `passC`:option: and `passL`:option: command line switches to something like: -.. code-block:: cmd + ```cmd nim c ... --d:nimAllocPagesViaMalloc --mm:orc --passC="-I$DEVKITPRO/libnx/include" ... --passL="-specs=$DEVKITPRO/libnx/switch.specs -L$DEVKITPRO/libnx/lib -lnx" + ``` or setup a ``nim.cfg`` file like so:: @@ -443,18 +444,18 @@ or setup a ``nim.cfg`` file like so:: --passL="-specs=$DEVKITPRO/libnx/switch.specs -L$DEVKITPRO/libnx/lib -lnx" The devkitPro setup must be the same as the default with their new installer -`here for Mac/Linux `_ or -`here for Windows `_. +[here for Mac/Linux](https://github.com/devkitPro/pacman/releases) or +[here for Windows](https://github.com/devkitPro/installer/releases). For example, with the above-mentioned config: -.. code:: cmd - + ```cmd nim c --os:nintendoswitch switchhomebrew.nim + ``` This will generate a file called ``switchhomebrew.elf`` which can then be turned into an nro file with the `elf2nro`:cmd: tool in the devkitPro release. Examples can be found at -`the nim-libnx github repo `_. +[the nim-libnx github repo](https://github.com/jyapayne/nim-libnx.git). There are a few things that don't work because the devkitPro libraries don't support them. They are: @@ -475,15 +476,15 @@ instance of the GC per process/address space. This instance is contained in ``nimrtl.dll``. This means that every generated Nim DLL depends on ``nimrtl.dll``. To generate the "nimrtl.dll" file, use the command: -.. code:: cmd - + ```cmd nim c -d:release lib/nimrtl.nim + ``` To link against ``nimrtl.dll`` use the command: -.. code:: cmd - + ```cmd nim c -d:useNimRtl myprog.nim + ``` **Note**: Currently the creation of ``nimrtl.dll`` with thread support has never been tested and is unlikely to work! @@ -511,7 +512,7 @@ Define Effect This only works with `--mm:none`:option:, `--mm:arc`:option: and `--mm:orc`:option:. `useRealtimeGC` Enables support of Nim's GC for *soft* realtime - systems. See the documentation of the `mm `_ + systems. See the documentation of the [mm](mm.html) for further information. `logGC` Enable GC logging to stdout. `nodejs` The JS target is actually ``node.js``. @@ -578,9 +579,9 @@ can be prevented and then via `--passL`:option: the static library can be linked against. For instance, to link statically against Lua this command might work on Linux: -.. code:: cmd - + ```cmd nim c --dynlibOverride:lua --passL:liblua.lib program.nim + ``` Backend language options @@ -590,7 +591,7 @@ The typical compiler usage involves using the `compile`:option: or `c`:option: command to transform a ``.nim`` file into one or more ``.c`` files which are then compiled with the platform's C compiler into a static binary. However, there are other commands to compile to C++, Objective-C, or JavaScript. More details -can be read in the `Nim Backend Integration document `_. +can be read in the [Nim Backend Integration document](backends.html). Nim documentation tools @@ -598,13 +599,13 @@ Nim documentation tools Nim provides the `doc`:idx: command to generate HTML documentation from ``.nim`` source files. Only exported symbols will appear in -the output. For more details `see the docgen documentation `_. +the output. For more details see [the docgen documentation](docgen.html). Nim idetools integration ======================== Nim provides language integration with external IDEs through the -idetools command. See the documentation of `idetools `_ +idetools command. See the documentation of [idetools](idetools.html) for further information. .. @@ -635,9 +636,9 @@ embedded microprocessors with only a few kilobytes of memory. A good start is to use the `any` operating target together with the `malloc` memory allocator and the `arc` garbage collector. For example: -.. code:: cmd - - nim c --os:any --mm:arc -d:useMalloc [...] x.nim + ```cmd + nim c --os:any --mm:arc -d:useMalloc [...] x.nim + ``` - `--mm:arc`:option: will enable the reference counting memory management instead of the default garbage collector. This enables Nim to use heap memory which @@ -665,7 +666,7 @@ The `--opt:size`:option: flag instructs Nim to optimize code generation for smal size (with the help of the C compiler), the `-flto`:option: flags enable link-time optimization in the compiler and linker. -Check the `Cross-compilation`_ section for instructions on how to compile the +Check the [Cross-compilation] section for instructions on how to compile the program for your target. @@ -716,8 +717,8 @@ Currently only Zephyr and FreeRTOS support these configurations. Nim for realtime systems ======================== -See the `--mm:arc` or `--mm:orc` memory management settings in `MM `_ for further -information. +See the `--mm:arc` or `--mm:orc` memory management settings in +[MM](mm.html) for further information. Signal handling in Nim @@ -760,28 +761,32 @@ passed to subroutines. The compiler does not copy strings that are a result of a procedure call, because the callee returns a new string anyway. Thus it is efficient to do: -.. code-block:: Nim + ```Nim var s = procA() # assignment will not copy the string; procA allocates a new # string already + ``` However, it is not efficient to do: -.. code-block:: Nim + ```Nim var s = varA # assignment has to copy the whole string into a new buffer! + ``` For `let` symbols a copy is not always necessary: -.. code-block:: Nim + ```Nim let s = varA # may only copy a pointer if it safe to do so + ``` If you know what you're doing, you can also mark single-string (or sequence) objects as `shallow`:idx:\: -.. code-block:: Nim + ```Nim var s = "abc" shallow(s) # mark 's' as a shallow string var x = s # now might not copy the string! + ``` Usage of `shallow` is always safe once you know the string won't be modified anymore, similar to Ruby's `freeze`:idx:. @@ -791,7 +796,7 @@ The compiler optimizes string case statements: A hashing scheme is used for them if several different string constants are used. So code like this is reasonably efficient: -.. code-block:: Nim + ```Nim case normalize(k.key) of "name": c.name = v of "displayname": c.displayName = v @@ -807,3 +812,4 @@ efficient: else: quit(errorStr(p, "expected: console or gui")) of "license": c.license = UnixToNativePath(k.value) else: quit(errorStr(p, "unknown variable: " & k.key)) + ``` diff --git a/doc/nimdoc.cls b/doc/nimdoc.cls index 271dc5dc9256..37039f1302a0 100644 --- a/doc/nimdoc.cls +++ b/doc/nimdoc.cls @@ -63,7 +63,7 @@ \usepackage{scrextend} % for the `addmargin` environment -\usepackage{xcolor} +\usepackage[table]{xcolor} \usepackage[urlbordercolor=blue,linkbordercolor=cyan, pdfborderstyle={/S/U/W 1}]{hyperref} \usepackage{enumitem} % for option list, enumList, and rstfootnote @@ -84,6 +84,11 @@ \definecolor{rstframecolor}{rgb}{0.85, 0.8, 0.6} +\usepackage{booktabs} +\belowrulesep=0ex +\aboverulesep=0ex +\renewcommand{\arraystretch}{1.1} + \newtcolorbox{rstprebox}[1][]{blanker, breakable, left=3mm, right=3mm, top=1mm, bottom=1mm, borderline ={0.1em}{0pt}{rstframecolor}, @@ -127,6 +132,7 @@ \newenvironment{rstoptlist}{% \begin{description}[font=\sffamily\bfseries,style=nextline,leftmargin=\rstoptleftmargin,labelwidth=\rstoptlabelwidth]}{\end{description}} +\usepackage{multirow} \usepackage{tabulary} % tables with adjustable cell width and no overflow % make tabulary prevent overflows (https://tex.stackexchange.com/a/195088) \tymin=60pt diff --git a/doc/nimdoc.css b/doc/nimdoc.css index 0014cf196719..f924f5a36f71 100644 --- a/doc/nimdoc.css +++ b/doc/nimdoc.css @@ -78,63 +78,45 @@ Modified by Boyd Greenfield and narimiran --clipboard-image: var(--clipboard-image-normal); } -.theme-switch-wrapper { - display: flex; - align-items: center; -} - -.theme-switch-wrapper em { - margin-left: 10px; - font-size: 1rem; -} - -.theme-switch { - display: inline-block; - height: 22px; - position: relative; - width: 50px; -} - -.theme-switch input { - display: none; -} - -.slider { - background-color: #ccc; - bottom: 0; - cursor: pointer; - left: 0; - position: absolute; - right: 0; - top: 0; - transition: .4s; -} - -.slider:before { - background-color: #fff; - bottom: 4px; - content: ""; - height: 13px; - left: 4px; - position: absolute; - transition: .4s; - width: 13px; -} - -input:checked + .slider { - background-color: #66bb6a; -} - -input:checked + .slider:before { - transform: translateX(26px); -} - -.slider.round { - border-radius: 17px; +@media (prefers-color-scheme: dark) { + [data-theme="auto"] { + --primary-background: #171921; + --secondary-background: #1e202a; + --third-background: #2b2e3b; + --info-background: #008000; + --warning-background: #807000; + --error-background: #c03000; + --border: #0e1014; + --text: #fff; + --anchor: #8be9fd; + --anchor-focus: #8be9fd; + --input-focus: #8be9fd; + --strong: #bd93f9; + --hint: #7A7C85; + --nim-sprite-base64: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAARMAAABMCAYAAABOBlMuAAAACXBIWXMAAAsTAAALEwEAmpwYAAAFFmlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNS42LWMxNDggNzkuMTY0MDM2LCAyMDE5LzA4LzEzLTAxOjA2OjU3ICAgICAgICAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgeG1sbnM6cGhvdG9zaG9wPSJodHRwOi8vbnMuYWRvYmUuY29tL3Bob3Rvc2hvcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RFdnQ9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZUV2ZW50IyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgMjEuMCAoV2luZG93cykiIHhtcDpDcmVhdGVEYXRlPSIyMDE5LTEyLTAzVDAxOjE4OjIyKzAxOjAwIiB4bXA6TW9kaWZ5RGF0ZT0iMjAxOS0xMi0wM1QwMToyMDoxMCswMTowMCIgeG1wOk1ldGFkYXRhRGF0ZT0iMjAxOS0xMi0wM1QwMToyMDoxMCswMTowMCIgZGM6Zm9ybWF0PSJpbWFnZS9wbmciIHBob3Rvc2hvcDpDb2xvck1vZGU9IjMiIHBob3Rvc2hvcDpJQ0NQcm9maWxlPSJzUkdCIElFQzYxOTY2LTIuMSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDplZGViMzU3MC1iNmZjLWQyNDQtYTExZi0yMjc5YmY4NDNhYTAiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6ZWRlYjM1NzAtYjZmYy1kMjQ0LWExMWYtMjI3OWJmODQzYWEwIiB4bXBNTTpPcmlnaW5hbERvY3VtZW50SUQ9InhtcC5kaWQ6ZWRlYjM1NzAtYjZmYy1kMjQ0LWExMWYtMjI3OWJmODQzYWEwIj4gPHhtcE1NOkhpc3Rvcnk+IDxyZGY6U2VxPiA8cmRmOmxpIHN0RXZ0OmFjdGlvbj0iY3JlYXRlZCIgc3RFdnQ6aW5zdGFuY2VJRD0ieG1wLmlpZDplZGViMzU3MC1iNmZjLWQyNDQtYTExZi0yMjc5YmY4NDNhYTAiIHN0RXZ0OndoZW49IjIwMTktMTItMDNUMDE6MTg6MjIrMDE6MDAiIHN0RXZ0OnNvZnR3YXJlQWdlbnQ9IkFkb2JlIFBob3Rvc2hvcCAyMS4wIChXaW5kb3dzKSIvPiA8L3JkZjpTZXE+IDwveG1wTU06SGlzdG9yeT4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz4JZNR8AAAfG0lEQVR4nO2deViTZ7r/7yxkJaxJ2MK+GCBAMCwS1kgUFQSKK4XWWqsz1jpjp3b0tDP1V+eqU391fqfT/mpPPd20drTFDS0KFEVWJSGAEgLIZpAICBJACIRs549Rj1WILAkBfD/XlevySp68z/0S3+/7vPdzLyidTgcLkU2bd+z39/f/q1gshsrKSoJELFCa2iaEuU9K6kb+8uXxv54/fzE8L/eswNT2zCfQpjbAGKS8lPFKSEjIXiaTCSEhIeDj4xNnapsQ5j6rktZGp6UlfxIdzQVzCplmanvmG1hTG2BIAtlc26CgoDfT0tL2e3l5AQCAjY0NkMnk/a9s2k6rrKw8UV8n1JjYTIQ5RlAw14KzmL3xze1vfJyUuMJaq9UCFovFm9qu+YbBxcSPFUYkk8l2Q0NDsvo6ocrQx5+I8Ih4bz6f/0l8fHyKlZXV4/dRKBQwmcwwMpn8A4FAoPgHhH9bV1sxa488wZxoaycnJ/a9e/duCa5fkc3WvAiTI4Ib77p+XdqHG9anbfLy8gAAgLGxMdBpF+bjvzExqJj4scKI0dHRnwQHB++orq7+AgDeMuTxJ2Jl4rqU9PT0EwEBAUQCgTDuGAaDAampqYepVKpHUHDk325Ulw0a266YuFW+Gzdu/MDPz29jfn7+XgA4aOw5ESZP6kvpCXv3vnM8NiaSamVl+fj9BepGNDoGFRN7e/slcXFxO1xcXMDJyWnH7j//H/fi4uJdgutXmgw5z5O8smn7X9euXbvf29sbMBjMhONQKBRYWVlBbGzsbjMzM3JoOG+/sKKwy1h2rd/4elpGRsYuLy+vaDweD2w2Oy1h5ZrCvEunEaeeiVnMiabyl/F2/+X9P+8JDPQHHA5napMWBAYTk6DgSNuEhIS9DAYDAP7tq1i6dOkqOp3OWbNu0wens44emeoxA9lcWwKBYEMkEm2JRKIdHo+3QKFQWJ1Op8ZgMER3d/dVq1evTnFycpr0MSkUCsTExGzH4/Gk1LTME/39/TI0Go1FoVCg1WrVY2NjipGRkcGRkRH5dPwrEZHLXMPCwjJSUlIy3dzcfB+97+rqGhYSEpIOAIiYmBguN3zL77dt3uPh4W5qUxYUBhMTb2/vjeHh4cvR6P/dILK0tITIyEg7BweHr363/Z3Ampqaf1Zcu/zMKiVsyVJvMplsRyKR7IhEor2FhYUbhUJhJCYm2pFIJB6JRAIymQx4PB7QaDRoNBowMzMDJycnwOOn7icjEokQGxu7icFgbLp///7jFY1WqwWlUgkjIyOgUCgO7Ni5Rz48PCwfHh7uGRkZeaBQKOSjo6ODCoVCXlNVKn/6uCsT13FXrVr1emho6BYKhfLMnP7+/omrU9LPX8g+UThloxEMxqJFXjxESAyPQcSEExrLWLNmzW57e/txP/fw8ABHR8cdDAaDt3xF2ru9vb03sVgs0cbGxs/FxWVZUlISj0aj+dna2oKtrS1M5PcwJCgUCry8vODRrs84vPfoH6OjoyCXy6Gvr+/R6+CWrX9s7evrk/b19bWr1Wqli4sLZ8OGDe95eXmxUSjUuAd0cHDwjoqK2sYKXFIhvnldYYTTQpgU4/8+jyASCYDGoCd+ZkYYF8OICYezl8PhuOkbQyAQIDo62s/NzS2np6cHbGxsgEajAYFAAAwGA1gsFia6CE0NgUAABwcHsLe3B61WC2q1eo9WqwWNRgNKpRLUajUQiUSgUCh6zwGHwwGTydzo5+eXBQBnZu8MEJ5keHhYPqyYWMtHR0ZBpVIhYj9FUDONgOUvT12+du3avMDAQJjssdRqNWCxCyrEZdLodDoQi8Ulx44de628NL/V1Pa8iERE8l2dHB2CJvpcq9Nqbt1qKURWj1Njxld0ZGTkAW9v70kLCQC8sEIC8O/HKx8fn2gmk8kHgCk7pRFmzrWyAikASE1tx0Jj2uH0EZHL/N7YtuvT4OBgzmz4OBYSeDweIiMjt2S++vtMP1YYEmmJsCCY8mNOIJtr6+zsHBcZGXmIw+G4mZubG8m0hU9HRwcUFxe/KxQKTyDRsQjznSmJCS9+dVRERMTfQ0NDo2xtbfUGiSFMjtHRUaitrc3Jzc09kHvxVLmp7UFAmC6oZQkvrZLL5RJhReHtiQb5scKIXC7371FRUX90dnYGIpE4JR8Jgn40Gg20t7fXFxYWfnr9+vWjz8sdYi+Osh4vzgUBwZSgtu94V+fs7Hx7YGCgra6u7khLS0u2RCwYeTQgKmYFh8fj/f/g4OAldnZ2prR1wdPd3Q1CofBQSUnJkdLi3N8E93FCY6k+Pj48FxcXjlar1ZSWlh65VvYr4kREmDNg79+/D3FxcW5OTk5uXl5evNbW1tL0jK3ZXV1d1ykUintycvInoaGhdkj+gvGxs7MDPp+/m0AgWMQvS/lyeHhYTqPRPJycnIJSU1NZ3t7eW2g0Gly/fv2oWq1Gij0hzClQ/gHhpLS0tEM8Hm/7I8Ho7++HlpYWsLa2Bg8PDxOb+OKhUCigqakJ7t+/D25ubuDu7g4oFAp0Oh08ePAAvv7666TTWUdzTG0nAsKTYMU3ryuSU18+4+bmFrZo0SIOAICVlRUsXrx4zkakLnRIJBI8CgJ8MtdJp9NBZ2enqL29XWRC8xAQxgUNAHD+3L8KGhoaCp78ABES04JCoX4jJAAAAwMDUFtbe96YpRMQEKbL41DU5ubmko6Ojj2PSgggzD36+/vrb9y4cX425zzw93/8EBjon2is44+NjSkePBjqGRwc7G5v7xBV19w8U5B/3qgrr9+/uWtXUuKKD/TZ9MXh/066/OuFmunO8dGBQ98HBbGSp/t9U6LRaDXK0dHBoeFhuVzeL22/0yFqamopufjLqRJ933ssJi0tLSXV1dWHGAzGbuObOzs8ubqa71vZKpUKOjo6blwpOF8zm/Mu5cVkLlkSaswprAHAaVihgK7O7oSGxltvfXLon3nXK4RHT2cdN4pfKDCAlZyUuMJan02nTmczAaBmunPw4qI3cbnh0/36XICq0+lgcPABp7OrK629vUP5z8++LLh2XXD05L++yxrvC4/F5EZ12WBS8saLS5Ys2U2lUufUY45SqQSlUgkqlQrUavXj19jYGGg0GtBoNKDT6UCn05VotVq1TqfToFAojFar1eh0Og0Wi8XhcDgeGo1+/PhgZmYGOBwOsFgsmJmZ/eY1F+nt7YXa2trs2Z73wdCQBgCMHp1IJpHA09MdPD3dLRIS+OtKisvWvbP7vf2lZdePVFwzbHTwyMiI3hidkZFRUKvUYzOZ48HQkBIA5nWqBAqFAktLC7C0tADmIh88Pz4uMSyUk7hn776DV4tKPn/6d/lNxp1MJqsRCASf8vn8XdMpOjRTVCoVjI2NgUqlAq1WCyMjI9DX1wf379+Hvr6+/Q8ePOgdGRmRKxSKx0WLFAqFXKlUKnQ6nUar1arHq47mxwrD4/F4Eg6HI2GxWDwej7cgkUjWFAqFam5uTjU3N6eRyeQPLSwswNraGqysrIBAIDwWFywW+zja11Qi29LSclIikeSZZPJZBovBAI8XA8HBQR9kZZ3lR8cmvFZSlGe00p8IkwONRkNERBj4+i7a4+XpHv307/IbMakWlciXJbx0nMPh7Jqo0JGh0el0MDo6Cl1dXSCVSkEmk7177969W319fe1DQ0M9KpVKoVarlWq1WjndNhUPG3ApAWDcOxLTLwSDwWAOotFoDBaLxRMIBAsrKysne3t7Xzqd7k2n0/c4OzsDlUoFHA4364IyMDAATU1NxdWikhcq6tXKyhJezljPJZKI2eERS5cZeoWCMD2srCwhPX0tVzk2djiCG//GtfLLUoBxShB0dHTU3Lx580sLC4vtJBLJKMZoNBqQSqUglUqPdnR01PT09DT19/fLHjx40DM0NNQ72933GiSVGgB4JFQK+LfoSAGgnL04yppEIh2xtLS0t7GxcaFSqR7Ozs4fMRgMcHR0nJX8pJs3b54Ui8UXjT7RHIRMIkFK8irfwcEHPwQELUmqvYHUGJkLmJubw8YNa/i9vfffY/px3myQiDTPiEl9nVDDX576jaenZ7SnpyfLUJNrNBqQyWRw+/bt4x0dHTdkMlltV1dXw/XygjkdEv4wB0YOAK0AUM70C8HQ6fSzdDrdm0qlejg6OrLc3Ny2MBiMadWjfR4PHjyAmzdvZs/1v5MxoVAokJK8iicWS95k+nH+s0EiQhqpzQGoVFtYk5a87ba0XQAA34xbpagg/5zoT7s/OGNnZ8eaaYkBuVwOnZ2d5VKpVNTS0lLS2NhYWFVZ3Dujg5qQh6uY+ocvCAiKIPn4+Jz19PSMdnV15VCpVL6Dg4NBViw6nQ5EItHRpqamqzM+2DzHzo4O69amftLQeKsAZrDLgmBY/PyYsCIhfs+SiKUFE5Y8EwqFx11cXDihoaFTjjFAoVAwPDwMHR0dourq6jNCofDHhZqUVnvjmgIAcgAgJyg40mLRokX8kJCQjT4+PussLS1n1JPl7t27UFxcfHguB6mNjY2B7G4naNRTWyygUCjAYDGAx+PB0sICSCSi3vFYLBbCwjjA8vddBQtATKb7d3saBwc7IJPJBpsHjUGDGRYLJBIJLK0sAfucmyIGg4FFi3y8AwNZtycUk5KiS02vvf7WWQaDkejg4DApQwAeh3xDaWnpPoFAcPxFqnP6sEvgGf+A8Bx3d/cvIyIiNi1evHjT8wpNj8fAwACUlZW9P9dD5+/ckcFbf9gd2dcnn9LNAovF4inmZHtXNxdOdBR3+/JlS33pdP29wolEInA4weuiYxOy5vvuTkeHDHb+8c8xvb33Z3R9/N+Df+uIjYk02DwkEsna2trS1d/fNyGeF7uTyw1/7g3R3t4O2OxA/TVghULhcQqFQk1JSfmYSNR/5wD4d6EfgUBwvLS09IhUKhW9qAV5H9YjKQwJi6uvrKw8ERoamhkSEpKp7w7yJEqlEiQSyZmysrJv53qjdaVSCZdyTk+3qFMrAJRHRPLPN95qeifj5fU7mYt8JhyMRqMhMJDFdnF25gDAvBYTpXIMWlpay2fq/8m5mDcIABYGnEcGAGI/VlhBZWX1yZdSkz55OX0dV5+7w9bGGvz8mPrFpK62QskJjf2GTqd7x8bGbpnID4BCoUAmk0lLSkqOiESik2UleS/MakQflYKrXQDQxY1a3tTe3i6KiIjY5OXlxX7e9+rr6wsuXbr0t4ffn9OgMWjghMZQRcLp+8GulRVI/QPC37Wxtnal0ajJtjY2E451ZjiBra31vE9lR2PQQKFQaAAwo98Yi8Xq9fpPd56HO6rlvKWJv/PwcK+JilyCmajWMw6HAzs7+rMFpQOCIn6zHywSFvXm5eUdFAqFZ9Rq9bgHa2trq79w4cK+zz49cAARkmcpL81v/a/Dhz49d+7c3qqqqjyVSjXuOJ1OBxKJpDw3N/fA5V+zax6978cKw/sHhM/raMrnUVdboSy4fPWQSFSjd5yFBQWIRNKEd2IEw1J4JUd88WL+R51d3XrHWVDMnxUTa2tr1zXrNiUGsrmPf7DS4tymCxcu7Kuurs55+kKQSqVN586d23vs+8NHDXUCC5Wzp3/Iy8rKeruysvLM2Nhvo7VVKhXU1tYWnj17du/T7UOdnZ2D7OzsfGGB09raVi4S1RzXl0eFw+EAj8chYjKLVFffyOrq1C8mJBLpWTFRKBRyDofzC4vFWvXk+1ev/CLOzs7eKxAIslQqFeh0Oujp6enKzs7em/XTd7OayTqfKb56sT4rK+sPAoHg5KO/o0KhAKFQmHXy5MkdF3/5+TeZmctXpIXZ29v7zqVcKWNRX1epuXu3U/y8pEw0GmndOZt0dnXVDw0P6/W5oNHoZ30mQ0NDPb29vfvj4+Pf3rR5B/7od188XnEUXr4gDgmL+0NfX5/U19d3d3l5+YGfTnyDtLmcIhXXLsu4UcvfR6PRGGtra9eysrIjYrE45+kt4Fheou/69es/unnz5vm7d+/Wmsre2WRkZGTQ1DYg/JYGiUiTm1ugBAC9IfHPiEmDpFITE7fqJI/H27lmzZpDq5LWtz55t6wUXO3ihMYerK+vz2tpaUFaM0yT8tL81ujYle+TSCTrvEunBU9/voTLd92wYcPHVCqV39XVdXCu7+oYCp1O90Kc50Jk3I5+xVcv1jc3N5d4enpSMzIyvkpK3sh78nORsKg3++yPBS/q1q+hKCm61DSekERGJ3ikp6d/ERsbm1xVVXWwtbX1hRFtFAqFPMLMUyZsDyoQCI7LZDKIiIjwzczM/GpV0vro2TTsRSUqZoX3+vXrP1u9enXi0NAQiESirIdRtggIc5oJ40zq6uryGhoa8ry8vBJCQ0O9USjU94mrN7yWc+EnvaXb5gJMvxCMp6cnl0Kh2Le1tZVXXLs8L1LXefGrWRkZGZ/x+XyeUqkEkUh0vqenZ14HZyG8OEwoJjdrygd37NxTEBkZmWBtbQ3BwcEeKBTq+/UbX3/355Pfzlmn66qk9dGbN29+k8PhbCSRSNDZ2Snb9ae/HCkpKTksEhbN2QTD5NSX+Vu3bj0cHBzsjcFg4O7du1BWVvbNwxB9BIQ5j94I2Fu3bhXW19cDl8sFLBYLHA7Hg0wmf/e77e84ffXlPz6fLSMnQ2paZkJ4eHjmtm3b+B4eHvZkMhlQKBTY29s72dvbfxgUFJT8x7ffP1NRUfHjXErnZ/qFYKKjo7dt3rz5g8DAQPtH/XHa2tpqGhsbC55/BASEuYFeMblz505NTU3NgfDw8PcwGAygUCjw9fW1IJPJn/1130Hv0tLSI4WXL4hny9inYS+Osvbz80tgMpn8jIwMPovFch2vpoiDgwM4ODhwfH19OYsWLeJv3/Hu+cbGxquzXZz5aZYlvMRJT0/fFhkZue3JZmfd3d0gEolOIr4ShPmEXjFpkFRqXlrzSnFnZ+d7Tk5OjzNfXVxcICMjY6ezszNnVdL6vU8HWhmbgKAIkrOzMyc1NTXz0YU4maAuOp0OK1as4EVFRfGEQqHg1dfePHzr1q2rs71S8WOF4f38/BLS09M/iIyM5DxdxLq5uVlcVVU1bgVwBIS5il4xAQCQyWRigUBwJikpKe3JVGQcDgdLly7l2tranti0ecf7IpEoy9hbxX6sMDydTvdevXr1ltjY2F3u7u6AxT73FJ7B3Nwc4uLiwthsdphQKCzZkL7l0/r6+oKbNeVG90+EhMXZL1++fFtycvKHrq6uz4igUqmE5ubmEiTHCWG+8dwrUXD9imz9xtd/jIuLS7N5KpsTjUZDUFCQE4PB+F4oFGYmJW888Mv5k4UTHGpGxC9LYaenp78VEhKyxdHRESgUyoyOh0KhwNraGuLi4qIDAgKi6+rqyjekb/mHMSN6N6RvSdu+ffseNpsdZm09ftuW+vp6EIvFSB9hhHnHpG7rUqm0orW1tdXS0tLj6TIEaDQaaDQaxMfH811dXTl/3Xfw+JUrVz411J01cfWG6IiIiC07d+5McHNzs7ewMGyOFw6HAwcHB6BSqVx3d/fwz7/4rkAgEBwXCoUnHpZonDGrU9J5MTEx27du3Zrm4uKC0beaqq6u/ry+vj7XEPMiIMwmkxKTimuXZe/u+fCkp6fnexPdUfF4PPj7+1szGIydLi4unF1/+kvenTt3RG1tbRXTqfma8lIG39/fP/HVV19NZrFYHpMpzjQTzMzMwNPTE+Pp6Zng6emZ4Ofnl5CesfV8bW1tznQe3/wDwvFeXl7Rvr6+Ca+88kpaUFCQh74GXzqdDrq7u6GpqankRQmdR1hYTNrhUFVVlcXj8d6ysrKy0OfstLS0hPj4eC6Xy+U2NzeDRCI5/sa2XeX37t1rGhwc7BoYGJBN1P+FFbiE5OzszGaxWImvvvrqpoCAAKfp+ERmCpPJBCaTmcnhcDJLS0u/TE59+YxUKhXoi/lg+oVgrKysGJaWlna2trYeaWlpXDabvTMgIGDSfp2KiorzbW1tL0zoPMLCYtJX6uVfs2u++PKowMPDgz+ZIslEIhECAgKAxWJlajSazJ6eHmhra4PW1tZvtmz9o6Czs7O+r6+vfWxsbFir1WosLCzsV6xYkcnj8d7z9vaelmPV0Hh5eYGnp+f2mJiY7UVFRZ/HL0v5tru7+5ZGo1FisVg8Docj4fF4CxsbG1c+nx/m7e39sYeHB7i4uIC5ufmU6r4ODQ1BZWXlifkSrYuA8DRTumIrKytPent78728vCb9HRQKBVgsFhwcHIBOpwObzd4yNja2RaVSwdDQEHR1dcHo6CjQaDRwdXWdsWPV0KBQKPDw8AA7O7udERERO2tra2FgYACoVCo4OTkBjUYDMpkMeDz+8WuqaLVaaGxsbL19+/YzSX8ICPOFqYrJidDQ0AwvLy/e80c/CwaDARKJBI86BdJoNHB3dwe1Wj0nViL6IJPJwGQywdnZGZRKJRAIBDBUx8OBgQEoLS39BtkORpjPTJg1PB61N64pmpqarvb39xvUiLkuJE9CJpPBxsbGYEICANDZ2SlHgtQQ5jtTEhMAgLq6ulyJRFJvDGNeREZGRkAikRSUFuci2cEI85opi0l+7hmBWCzOeV6dToTJcfv27cHr168jxbgR5j1TFhMAgObm5hKZDNl0MAQtLS3Xzpw6hkS8Isx7piUmUqlUIBAIJuyjgzA5Ojs7QSKRINGuCAuCaYmJsKKw68qVK59KJJIu5HFneiiVSigqKjouEolOmtoWBARDMC0xAQC4+MvPJadOnXq3ra1N8yL0dDEkOp0OSktLy/Pz8w8+3d4CAWG+Mm0xAQA4fuy/jl+8ePGju3fvGsqeBY9Wq4XKysrWU6dOvX31yi8mKyyFgGBoZiQmAAD/79D+fadPn96PCMrz0el0UFVV1frtt9+mj9fiAgFhPjNjMQEAyMvLO3Ds2LE/tLS0INmuerh27Vr9999//xoiJAgLEYOEntbVVigB4PNNm3cMpqSkfMRms50McdyFgkqlgqKiovJTp069nZ97BhEShAWJQePYj373xdF1GzbLFQrFx6Ghob766ne8KNy7dw+KiopO5ubmfmTK4tsICMbG4EkxWT99d35l4rre/v7+D0NCQvh0Ot3QU8wL1Go1SKVSTX5+/sH8/PyDSP8bhIWOUTLsLuVklQcFR65pbGzcvnLlyvfc3NwsCASCMaaac+h0OhgaGoLq6uqaCxcu/OV01tGcTw7uM7VZCAhGx2jpug/vxAd58atzoqKitq1cuXKnvb29saabE+h0Oqiurpbm5eUdrK6uPlspuDrvY0hmO4YIhUIBGq1/X2CmNqFQKL3/79HomZ/z82xEowyy9zFr80zGDqPn/hdeviBmL47ad+fOnRsRERGbQkNDo62srIw97azT2dkJxcXFx0tKSo7Mdh8hY4LD4TDPH2U4MFjMc6tLmZmZzaj+Aw6H0/t9PB4PGCxmRudNJBL0ngeZTAI0Gj3jv+1szfM88Hic8cUEAKCmqlQOAN/ELU2qkEgkySwWK3HRokVcBoMxG9MbDZ1OB83NzdDU1FRQW1t7XiAQHJ+ovu18pbr6Rg6L5ZtoM0EhcUPT0tJW8tWRb0vQqIkvgKqqmhnVfrl2TfANXo+gjKlUio4OWc1M5sjOzjnQUH8rbqLPu3t6moaGhmfc+3q25tGHUqmECoEIUKbIrVkcEkONiIh4jcvlvu7s7OxLo9GmVe7QVCgUCujq6oKGhoaCioqKo9XV1WeM3YDMVPDik1gpyas+XrVyeaKXl8czjyANjbcgI/MNmkg49Q4ECPOH3NyC4RUr+M8IcHt7B1y9WlKRl3/5kElKnD1sfXEoJCzueEBAQGJYWFgGk8nk2djYAIFAgLm4pTw6Ogqjo6Mgl8vhxo0b50tLS4/U19fnLvS2FIWXfxEDQNLmLW9ueW1TxtchHDaQyWRTm4VgYkZHR6G+vhF+/NfP+y5e+vVjiVgwZpKVydOwF0dZW1lZOTGZTD6bzU4LCAiIptPp8HTDL1MwOjoKLS0tUFdXd1IsFudIpdKKgYGB7tloJTrX4MUnsVJTEj9etzY10dHRAQAAGm81wcsZW5CVyQInL69gNCGBjwcAGBx8ANnncypOnTr3H9nn/reD55wovvrQpyIHAHFUzIocGo3mQaPRfBwdHVlubm7bXF1dgcFgABqNNvruglwuh7t374JMJoOOjo7P79y5I+ru7m7q7e1tXQi7MzOh8PIv4pCw2DdaWtte37Au7aPIyCWAxWABjUbPif9HCMbjURtKiaQBfvr5zH9evlJ0uLQ4r/nJMXNiZTIRrMAlJAcHB18HBweWo6Mjy8rKajeJRAJLS0uwtLQECwsLoFAogMfjAYvFgpmZ2XNXMyqVCoaHh2FoaAiGh4cfvwYGBqCvrw+6u7vfvnfvXlNvb29rT09Pq0QsUM7S6c4rNqS/lrZ5U+YPRBKR9M7u9xwqBUUvtNAudH766XSLE8PR49ixE78/8tVnX403Zk7fUR46NUUAIPIPCMdTKJTdNjY2QKPRgE6nA51OB1tbWyCRSIDD4YBAIAAejwcCgfDYUajVakGlUoFarQadTvfY79HX1wf9/f0gl8tBLpfDvXv3HvXw+dxQPYYXMj+d+P7Mmzv+5OHr6/OJWq1GBHeB09TcUiKuq/coKS3/eqIx/wPkiIXC3w6YjAAAAABJRU5ErkJggg=="); + + --keyword: #ff79c6; + --identifier: #f8f8f2; + --comment: #6272a4; + --operator: #ff79c6; + --punctuation: #f8f8f2; + --other: #f8f8f2; + --escapeSequence: #bd93f9; + --number: #bd93f9; + --literal: #f1fa8c; + --program: #9090c0; + --option: #90b010; + --raw-data: #8be9fd; + + --clipboard-image-normal: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' style='color: lightgray' fill='none' viewBox='0 0 24 24' stroke='currentColor'%3E %3Cpath stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2' /%3E %3C/svg%3E"); + --clipboard-image-selected: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' style='color: lightgray' viewBox='0 0 20 20' fill='currentColor'%3E %3Cpath d='M8 3a1 1 0 011-1h2a1 1 0 110 2H9a1 1 0 01-1-1z' /%3E %3Cpath d='M6 3a2 2 0 00-2 2v11a2 2 0 002 2h8a2 2 0 002-2V5a2 2 0 00-2-2 3 3 0 01-3 3H9a3 3 0 01-3-3z' /%3E %3C/svg%3E"); + --clipboard-image: var(--clipboard-image-normal); + } } -.slider.round:before { - border-radius: 50%; +.theme-select-wrapper { + display: flex; + align-items: center; } html { @@ -159,24 +141,30 @@ body { padding: 0; box-sizing: border-box; } -.column, -.columns { +.column, .columns { width: 100%; float: left; box-sizing: border-box; - margin-left: 1%; -} + margin-left: 1%; } -.column:first-child, -.columns:first-child { +.column:first-child, .columns:first-child { margin-left: 0; } +.container .row { + display: flex; } + .three.columns { - width: 22%; + width: 25.0%; + height: 100vh; + position: sticky; + top: 0px; + overflow-y: auto; + padding: 2px; } .nine.columns { - width: 77.0%; } + width: 75.0%; + padding-left: 1.5em; } .twelve.columns { width: 100%; @@ -269,25 +257,26 @@ a.nimdoc { a.toc-backref { text-decoration: none; - color: var(--text); } + color: var(--text); +} a.link-seesrc { color: #607c9f; font-size: 0.9em; - font-style: italic; } + font-style: italic; +} -a:hover, -a:focus { +a:hover, a:focus { color: var(--anchor-focus); - text-decoration: underline; } + text-decoration: underline; +} a:hover span.Identifier { color: var(--anchor); } -sub, -sup { +sub, sup { position: relative; font-size: 75%; line-height: 0; @@ -314,8 +303,7 @@ img { background: transparent !important; box-shadow: none !important; } - a, - a:visited { + a, a:visited { text-decoration: underline; } a[href]:after { @@ -329,16 +317,14 @@ img { a[href^="#"]:after { content: ""; } - pre, - blockquote { + pre, blockquote { border: 1px solid #999; page-break-inside: avoid; } thead { display: table-header-group; } - tr, - img { + tr, img { page-break-inside: avoid; } img { @@ -353,22 +339,18 @@ img { h1.title { page-break-before: avoid; } - p, - h2, - h3 { + p, h2, h3 { orphans: 3; widows: 3; } - h2, - h3 { + h2, h3 { page-break-after: avoid; } } p { margin-top: 0.5em; - margin-bottom: 0.5em; -} + margin-bottom: 0.5em; } small { font-size: 85%; } @@ -376,8 +358,7 @@ small { strong { font-weight: 600; font-size: 0.95em; - color: var(--strong); -} + color: var(--strong); } em { font-style: italic; } @@ -398,8 +379,7 @@ h1.title { text-align: center; font-weight: 900; margin-top: 0.75em; - margin-bottom: 0em; -} + margin-bottom: 0em; } h2 { font-size: 1.3em; @@ -426,36 +406,29 @@ h6 { font-size: 1.1em; } -ul, -ol { +ul, ol { padding: 0; margin-top: 0.5em; margin-left: 0.75em; } -ul ul, -ul ol, -ol ol, -ol ul { +ul ul, ul ol, ol ol, ol ul { margin-bottom: 0; margin-left: 1.25em; } ul.simple > li { - list-style-type: circle; -} + list-style-type: circle; } ul.simple-boot li { - list-style-type: none; - margin-left: 0em; - margin-bottom: 0.5em; -} + list-style-type: none; + margin-left: 0em; + margin-bottom: 0.5em; } ol.simple > li, ul.simple > li { margin-bottom: 0.2em; margin-left: 0.4em } ul.simple.simple-toc > li { - margin-top: 1em; -} + margin-top: 1em; } ul.simple-toc { list-style: none; @@ -464,8 +437,7 @@ ul.simple-toc { margin-top: 1em; } ul.simple-toc > li { - list-style-type: none; -} + list-style-type: none; } ul.simple-toc-section { list-style-type: circle; @@ -475,12 +447,10 @@ ul.simple-toc-section { ul.nested-toc-section { list-style-type: circle; margin-left: -0.75em; - color: var(--text); -} + color: var(--text); } ul.nested-toc-section > li { - margin-left: 1.25em; -} + margin-left: 1.25em; } ol.arabic { @@ -527,7 +497,8 @@ hr.footnote { margin-top: 0.15em; } div.footnote-group { - margin-left: 1em; } + margin-left: 1em; +} div.footnote-label { display: inline-block; min-width: 1.7em; @@ -611,7 +582,7 @@ pre { border: 1px solid var(--border); -webkit-border-radius: 6px; -moz-border-radius: 6px; - border-radius: 6px; + border-radius: 6px; } .copyToClipBoardBtn { @@ -629,7 +600,7 @@ pre { .copyToClipBoard:hover .copyToClipBoardBtn { visibility: visible; -} +} .pre-scrollable { max-height: 340px; @@ -694,8 +665,8 @@ table th { font-weight: bold; } table th.docinfo-name { - background-color: transparent; - text-align: right; + background-color: transparent; + text-align: right; } table tr:hover { @@ -712,31 +683,31 @@ table.borderless td, table.borderless th { padding: 0 0.5em 0 0 !important; } .admonition { - padding: 0.3em; - background-color: var(--secondary-background); - border-left: 0.4em solid #7f7f84; - margin-bottom: 0.5em; - -webkit-box-shadow: 0 5px 8px -6px rgba(0,0,0,.2); - -moz-box-shadow: 0 5px 8px -6px rgba(0,0,0,.2); - box-shadow: 0 5px 8px -6px rgba(0,0,0,.2); + padding: 0.3em; + background-color: var(--secondary-background); + border-left: 0.4em solid #7f7f84; + margin-bottom: 0.5em; + -webkit-box-shadow: 0 5px 8px -6px rgba(0,0,0,.2); + -moz-box-shadow: 0 5px 8px -6px rgba(0,0,0,.2); + box-shadow: 0 5px 8px -6px rgba(0,0,0,.2); } .admonition-info { - border-color: var(--info-background); + border-color: var(--info-background); } .admonition-info-text { - color: var(--info-background); + color: var(--info-background); } .admonition-warning { - border-color: var(--warning-background); + border-color: var(--warning-background); } .admonition-warning-text { - color: var(--warning-background); + color: var(--warning-background); } .admonition-error { - border-color: var(--error-background); + border-color: var(--error-background); } .admonition-error-text { - color: var(--error-background); + color: var(--error-background); } .first { @@ -770,8 +741,7 @@ div.footer, div.header { font-size: smaller; } div.footer { - padding-top: 5em; -} + padding-top: 5em; } div.line-block { display: block; @@ -790,17 +760,14 @@ div.search_results { background-color: var(--third-background); margin: 3em; padding: 1em; - border: 1px solid #4d4d4d; -} + border: 1px solid #4d4d4d; } div#global-links ul { margin-left: 0; - list-style-type: none; -} + list-style-type: none; } div#global-links > simple-boot { - margin-left: 3em; -} + margin-left: 3em; } hr.docutils { width: 75%; } @@ -980,8 +947,7 @@ span.Directive { span.option { font-weight: bold; font-family: "Source Code Pro", Monaco, Menlo, Consolas, "Courier New", monospace; - color: var(--option); -} + color: var(--option); } span.Prompt { font-weight: bold; @@ -997,11 +963,10 @@ span.program { text-decoration: underline; text-decoration-color: var(--hint); text-decoration-thickness: 0.05em; - text-underline-offset: 0.15em; -} + text-underline-offset: 0.15em; } -span.Command, span.Rule, span.Hyperlink, span.Label, span.Reference, -span.Other { +span.Command, span.Rule, span.Hyperlink, +span.Label, span.Reference, span.Other { color: var(--other); } /* Pop type, const, proc, and iterator defs in nim def blocks */ @@ -1039,17 +1004,14 @@ span.pragmadots { border-radius: 4px; margin: 0 2px; cursor: pointer; - font-size: 0.8em; -} + font-size: 0.8em; } span.pragmadots:hover { - background-color: var(--hint); -} + background-color: var(--hint); } + span.pragmawrap { - display: none; -} + display: none; } span.attachedType { display: none; - visibility: hidden; -} + visibility: hidden; } diff --git a/doc/nimfix.rst b/doc/nimfix.md similarity index 100% rename from doc/nimfix.rst rename to doc/nimfix.md diff --git a/doc/nimgrep.md b/doc/nimgrep.md new file mode 100644 index 000000000000..8fb86a9d3865 --- /dev/null +++ b/doc/nimgrep.md @@ -0,0 +1,128 @@ +========================= + nimgrep User's manual +========================= + +:Author: Andreas Rumpf +:Version: 1.6.0 + +.. default-role:: option +.. contents:: + +Nimgrep is a command line tool for search and replace tasks. It can search for +regex or peg patterns and can search whole directories at once. User +confirmation for every single replace operation can be requested. + +Nimgrep has particularly good support for Nim's +eccentric *style insensitivity* (see option `-y` below). +Apart from that it is a generic text manipulation tool. + + +Installation +============ + +Compile nimgrep with the command: + + ```cmd + nim c -d:release tools/nimgrep.nim + ``` + +And copy the executable somewhere in your ``$PATH``. + + +Command line switches +===================== + +.. include:: nimgrep_cmdline.txt + +Path filter options +------------------- + +Let us assume we have file `dirA/dirB/dirC/file.nim`. +Filesystem path options will match for these parts of the path: + +| option | matches for | +| :------------------ | :-------------------------------- | +| `--[not]extensions` | ``nim`` | +| `--[not]filename` | ``file.nim`` | +| `--[not]dirname` | ``dirA`` and ``dirB`` and ``dirC`` | +| `--[not]dirpath` | ``dirA/dirB/dirC`` | + +Combining multiple filter options together and negating them +------------------------------------------------------------ + +Options for filtering can be provided multiple times so they form a list, +which works as: +* positive filters + `--filename`, `--dirname`, `--dirpath`, `--inContext`, + `--inFile` accept files/matches if *any* pattern from the list is hit +* negative filters + `--notfilename`, `--notdirname`, `--notdirpath`, `--notinContext`, + `--notinFile` accept files/matches if *no* pattern from the list is hit. + +In other words the same filtering option repeated many times means logical OR. + +.. Important:: + Different filtering options are related by logical AND: they all must + be true for a match to be accepted. + E.g. `--filename:F --dirname:D1 --notdirname:D2` means + `filename(F) AND dirname(D1) AND (NOT dirname(D2))`. + +So negative filtering patterns are effectively related by logical OR also: +`(NOT PAT1) AND (NOT PAT2) == NOT (PAT1 OR PAT2)`:literal: in pseudo-code. + +That means you can always use only 1 such an option with logical OR, e.g. +`--notdirname:PAT1 --notdirname:PAT2` is fully equivalent to +`--notdirname:'PAT1|PAT2'`. + +.. Note:: + If you want logical AND on patterns you should compose 1 appropriate pattern, + possibly combined with multi-line mode `(?s)`:literal:. + E.g. to require that multi-line context of matches has occurences of + **both** PAT1 and PAT2 use positive lookaheads (`(?=PAT)`:literal:): + ```cmd + nimgrep --inContext:'(?s)(?=.*PAT1)(?=.*PAT2)' + ``` + +Meaning of `^`:literal: and `$`:literal: +======================================== + +`nimgrep`:cmd: PCRE engine is run in a single-line mode so +`^`:literal: matches the beginning of whole input *file* and +`$`:literal: matches the end of *file* (or whole input *string* for +options like `--filename`). + +Add the `(?m)`:literal: modifier to the beginning of your pattern for +`^`:literal: and `$`:literal: to match the beginnings and ends of *lines*. + +Examples +======== + +All examples below use default PCRE Regex patterns: + ++ To search recursively in Nim files using style-insensitive identifiers: + + ```cmd + nimgrep --recursive --ext:'nim|nims' --ignoreStyle + # short: -r --ext:'nim|nims' -y + ``` + + .. Note:: we used `'` quotes to avoid special treatment of `|` symbol + for shells like Bash + ++ To exclude version control directories (Git, Mercurial=hg, Subversion=svn) + from the search: + ```cmd + nimgrep --notdirname:'^\.git$' --notdirname:'^\.hg$' --notdirname:'^\.svn$' + # short: --ndi:'^\.git$' --ndi:'^\.hg$' --ndi:'^\.svn$' + ``` ++ To search only in paths containing the `tests`:literal: sub-directory + recursively: + ```cmd + nimgrep --recursive --dirname:'^tests$' + # short: -r --di:'^tests$' + # or using --dirpath: + nimgrep --recursive --dirpath:'(^|/)tests($|/)' + # short: -r --pa:'(^|/)tests($|/)' + ``` ++ Nimgrep can search multi-line, e.g. to find files containing `import`:literal: + and then `strutils`:literal: use pattern `'import(.|\n)*?strutils'`:literal:. diff --git a/doc/nimgrep.rst b/doc/nimgrep.rst deleted file mode 100644 index 6088a4c45859..000000000000 --- a/doc/nimgrep.rst +++ /dev/null @@ -1,69 +0,0 @@ -========================= - nimgrep User's manual -========================= - -:Author: Andreas Rumpf -:Version: 1.6.0 - -.. default-role:: option -.. contents:: - -Nimgrep is a command line tool for search and replace tasks. It can search for -regex or peg patterns and can search whole directories at once. User -confirmation for every single replace operation can be requested. - -Nimgrep has particularly good support for Nim's -eccentric *style insensitivity* (see option `-y` below). -Apart from that it is a generic text manipulation tool. - - -Installation -============ - -Compile nimgrep with the command: - -.. code:: cmd - nim c -d:release tools/nimgrep.nim - -And copy the executable somewhere in your ``$PATH``. - - -Command line switches -===================== - -.. include:: nimgrep_cmdline.txt - -Examples -======== - -All examples below use default PCRE Regex patterns: - -+ To search recursively in Nim files using style-insensitive identifiers: - - .. code:: cmd - nimgrep --recursive --ext:'nim|nims' --ignoreStyle - # short: -r --ext:'nim|nims' -y - - .. Note:: we used `'` quotes to avoid special treatment of `|` symbol - for shells like Bash - -+ To exclude version control directories (Git, Mercurial=hg, Subversion=svn) - from the search: - - .. code:: cmd - nimgrep --excludeDir:'^\.git$' --excludeDir:'^\.hg$' --excludeDir:'^\.svn$' - # short: --ed:'^\.git$' --ed:'^\.hg$' --ed:'^\.svn$' - -+ To search only in paths containing the `tests` sub-directory recursively:: - - .. code:: cmd - nimgrep --recursive --includeDir:'(^|/)tests($|/)' - # short: -r --id:'(^|/)tests($|/)' - - .. Attention:: note the subtle difference between `--excludeDir`:option: and - `--includeDir`:option:\: the former is applied to relative directory entries - and the latter is applied to the whole paths - -+ Nimgrep can search multi-line, e.g. to find files containing `import` - and then `strutils` use pattern `'import(.|\n)*?strutils'`:option:. - diff --git a/doc/nimgrep_cmdline.txt b/doc/nimgrep_cmdline.txt index bbcbcf530e63..73f29f524525 100644 --- a/doc/nimgrep_cmdline.txt +++ b/doc/nimgrep_cmdline.txt @@ -30,7 +30,7 @@ Positional arguments, from left to right: For any given DIRECTORY nimgrep searches only its immediate files without - traversing sub-directories unless `--recursive` is specified. + traversing subdirectories unless `--recursive` is specified. In replacement mode we require all 3 positional arguments to avoid damaging. @@ -46,44 +46,60 @@ Options: nimgrep --filenames # In current dir nimgrep --filenames "" DIRECTORY # Note empty pattern "", lists all files in DIRECTORY - * Interprete patterns: --peg PATTERN and PAT are Peg --re PATTERN and PAT are regular expressions (default) --rex, -x use the "extended" syntax for the regular expression so that whitespace is not significant --word, -w matches should have word boundaries (buggy for pegs!) - --ignoreCase, -i be case insensitive in PATTERN and PAT + --ignoreCase, -i be case-insensitive in PATTERN and PAT --ignoreStyle, -y be style insensitive in PATTERN and PAT - .. Note:: PATERN and patterns PAT (see below in other options) are all either + .. Note:: PATTERN and patterns PAT (see below in other options) are all either Regex or Peg simultaneously and options `--rex`, `--word`, `--ignoreCase`, and `--ignoreStyle` are applied to all of them. * File system walk: --recursive, -r process directories recursively --follow follow all symlinks when processing recursively - --ext:EX1|EX2|... only search the files with the given extension(s), - empty one ("--ext") means files with missing extension - --noExt:EX1|... exclude files having given extension(s), use empty one to - skip files with no extension (like some binary files are) - --includeFile:PAT search only files whose names contain pattern PAT - --excludeFile:PAT skip files whose names contain pattern PAT - --includeDir:PAT search only files with their whole directory path - containing PAT - --excludeDir:PAT skip directories whose name (not path) - contain pattern PAT - --if,--ef,--id,--ed abbreviations of the 4 options above --sortTime, -s[:asc|desc] order files by the last modification time (default: off): ascending (recent files go last) or descending -* Filter file content: - --match:PAT select files containing a (not displayed) match of PAT - --noMatch:PAT select files not containing any match of PAT +* Filter files (based on filesystem paths): + + .. Hint:: Instead of `not` you can type just `n` for negative options below. + + --ex[tensions]:EX1|EX2|... + only search the files with the given extension(s), + empty one (`--ex`) means files with missing extension + --notex[tensions]:EX1|EX2|... + exclude files having given extension(s), use empty one to + skip files with no extension (like some binary files are) + --fi[lename]:PAT search only files whose name matches pattern PAT + --notfi[lename]:PAT skip files whose name matches pattern PAT + --di[rname]:PAT select files that in their path have a directory name + that matches pattern PAT + --notdi[rname]:PAT do not descend into directories whose name (not path) + matches pattern PAT + --dirp[ath]:PAT select only files whose whole relative directory path + matches pattern PAT + --notdirp[ath]:PAT skip files whose whole relative directory path + matches pattern PAT + +* Filter files (based on file contents): + --inF[ile]:PAT select files containing a (not displayed) match of PAT + --notinF[ile]:PAT skip files containing a match of PAT --bin:on|off|only process binary files? (detected by \0 in first 1K bytes) (default: on - binary and text files treated the same way) --text, -t process only text files, the same as `--bin:off` +* Filter matches: + --inC[ontext]:PAT select only matches containing a match of PAT in their + surrounding context (multiline with `-c`, `-a`, `-b`) + --notinC[ontext]:PAT + skip matches not containing a match of PAT + in their surrounding context + * Represent results: --nocolor output will be given without any colors --color[:on] force color even if output is redirected (default: auto) diff --git a/doc/niminst.rst b/doc/niminst.md similarity index 87% rename from doc/niminst.rst rename to doc/niminst.md index 8fff1f0e8133..b1bbade1463d 100644 --- a/doc/niminst.rst +++ b/doc/niminst.md @@ -14,7 +14,7 @@ Introduction niminst is a tool to generate an installer for a Nim program. Currently it can create an installer for Windows -via `Inno Setup `_ as well as +via [Inno Setup](http://www.jrsoftware.org/isinfo.php) as well as installation/deinstallation scripts for UNIX. Later versions will support Linux' package management systems. @@ -26,7 +26,7 @@ systems. Configuration file ================== -niminst uses the Nim `parsecfg `_ module to parse the +niminst uses the Nim [parsecfg](parsecfg.html) module to parse the configuration file. Here's an example of how the syntax looks like: .. include:: mytest.cfg @@ -49,20 +49,20 @@ contain the following key-value pairs: ==================== ======================================================= Key description ==================== ======================================================= -`Name` the project's name; this needs to be a single word -`DisplayName` the project's long name; this can contain spaces. If +`Name` the project's name; this needs to be a single word +`DisplayName` the project's long name; this can contain spaces. If not specified, this is the same as `Name`. -`Version` the project's version -`OS` the OSes to generate C code for; for example: +`Version` the project's version +`OS` the OSes to generate C code for; for example: `"windows;linux;macosx"` -`CPU` the CPUs to generate C code for; for example: +`CPU` the CPUs to generate C code for; for example: `"i386;amd64;powerpc"` -`Authors` the project's authors -`Description` the project's description -`App` the application's type: "Console" or "GUI". If +`Authors` the project's authors +`Description` the project's description +`App` the application's type: "Console" or "GUI". If "Console", niminst generates a special batch file for Windows to open up the command-line shell. -`License` the filename of the application's license +`License` the filename of the application's license ==================== ======================================================= @@ -149,9 +149,9 @@ Possible options are: ==================== ======================================================= Key description ==================== ======================================================= -`InstallScript` boolean flag whether an installation shell script +`InstallScript` boolean flag whether an installation shell script should be generated. Example: `InstallScript: "Yes"` -`UninstallScript` boolean flag whether a de-installation shell script +`UninstallScript` boolean flag whether a de-installation shell script should be generated. Example: `UninstallScript: "Yes"` ==================== ======================================================= diff --git a/doc/nims.rst b/doc/nims.md similarity index 79% rename from doc/nims.rst rename to doc/nims.md index 4141c3bdc002..ecda526fb28d 100644 --- a/doc/nims.rst +++ b/doc/nims.md @@ -29,7 +29,7 @@ previous settings): ``$project.nim``. This file can be skipped with the same `--skipProjCfg`:option: command line option. -For available procs and implementation details see `nimscript `_. +For available procs and implementation details see [nimscript](nimscript.html). Limitations @@ -61,52 +61,53 @@ Standard library modules At least the following standard library modules are available: -* `macros `_ -* `os `_ -* `strutils `_ -* `math `_ -* `distros `_ -* `sugar `_ -* `algorithm `_ -* `base64 `_ -* `bitops `_ -* `chains `_ -* `colors `_ -* `complex `_ -* `htmlgen `_ -* `httpcore `_ -* `lenientops `_ -* `mersenne `_ -* `options `_ -* `parseutils `_ -* `punycode `_ -* `random `_ -* `stats `_ -* `strformat `_ -* `strmisc `_ -* `strscans `_ -* `unicode `_ -* `uri `_ -* `std/editdistance `_ -* `std/wordwrap `_ -* `std/sums `_ -* `parsecsv `_ -* `parsecfg `_ -* `parsesql `_ -* `xmlparser `_ -* `htmlparser `_ -* `ropes `_ -* `json `_ -* `parsejson `_ -* `strtabs `_ -* `unidecode `_ - -In addition to the standard Nim syntax (`system `_ module), +* [macros](macros.html) +* [os](os.html) +* [strutils](strutils.html) +* [math](math.html) +* [distros](distros.html) +* [sugar](sugar.html) +* [algorithm](algorithm.html) +* [base64](base64.html) +* [bitops](bitops.html) +* [chains](chains.html) +* [colors](colors.html) +* [complex](complex.html) +* [htmlgen](htmlgen.html) +* [httpcore](httpcore.html) +* [lenientops](lenientops.html) +* [mersenne](mersenne.html) +* [options](options.html) +* [parseutils](parseutils.html) +* [punycode](punycode.html) +* [random](punycode.html) +* [stats](stats.html) +* [strformat](strformat.html) +* [strmisc](strmisc.html) +* [strscans](strscans.html) +* [unicode](unicode.html) +* [uri](uri.html) +* [std/editdistance](editdistance.html) +* [std/wordwrap](wordwrap.html) +* [std/sums](sums.html) +* [parsecsv](parsecsv.html) +* [parsecfg](parsecfg.html) +* [parsesql](parsesql.html) +* [xmlparser](xmlparser.html) +* [htmlparser](htmlparser.html) +* [ropes](ropes.html) +* [json](json.html) +* [parsejson](parsejson.html) +* [strtabs](strtabs.html) +* [unidecode](unidecode.html) + +In addition to the standard Nim syntax ([system](system.html) module), NimScripts support the procs and templates defined in the -`nimscript `_ module too. +[nimscript](nimscript.html) module too. See also: -* `Check the tests for more information about modules compatible with NimScript. `_ +* [Check the tests for more information about modules compatible with NimScript]( + https://github.com/nim-lang/Nim/blob/devel/tests/test_nimscript.nims) NimScript as a configuration file @@ -118,22 +119,24 @@ NimScript. Similarly, command-line `--FOO:VAL`:option: translates to Here are few examples of using the `switch` proc: -.. code-block:: nim + ```nim # command-line: --opt:size switch("opt", "size") # command-line: --define:release or -d:release switch("define", "release") # command-line: --forceBuild switch("forceBuild") + ``` NimScripts also support `--`:option: templates for convenience, which look like command-line switches written as-is in the NimScript file. So the above example can be rewritten as: -.. code-block:: nim + ```nim --opt:size --define:release --forceBuild + ``` **Note**: In general, the *define* switches can also be set in NimScripts using `switch` or `--`, as shown in above examples. Few @@ -148,9 +151,10 @@ The `task` template that the `system` module defines allows a NimScript file to be used as a build tool. The following example defines a task `build` that is an alias for the `c`:option: command: -.. code-block:: nim + ```nim task build, "builds an example": setCommand "c" + ``` In fact, as a convention the following tasks should be available: @@ -166,14 +170,14 @@ Task Description ========= =================================================== -Look at the module `distros `_ for some support of the +Look at the module [distros](distros.html) for some support of the OS's native package managers. Nimble integration ================== -See the `Nimble readme `_ +See the [Nimble readme](https://github.com/nim-lang/nimble#readme) for more information. @@ -184,8 +188,7 @@ NimScript can also be used directly as a portable replacement for Bash and Batch files. Use `nim myscript.nims`:cmd: to run ``myscript.nims``. For example, installation of Nimble could be accomplished with this simple script: -.. code-block:: nim - + ```nim mode = ScriptMode.Verbose var id = 0 @@ -198,16 +201,17 @@ installation of Nimble could be accomplished with this simple script: exec "nim c nimble" mvFile "nimble" & $id & "/src/nimble".toExe, "bin/nimble".toExe + ``` On Unix, you can also use the shebang `#!/usr/bin/env nim`, as long as your filename ends with ``.nims``: -.. code-block:: nim - + ```nim #!/usr/bin/env nim mode = ScriptMode.Silent echo "hello world" + ``` Use `#!/usr/bin/env -S nim --hints:off` to disable hints. @@ -229,8 +233,7 @@ allowing the same script to support a lot of systems. See the following (incomplete) example: -.. code-block:: nim - + ```nim import std/distros # Architectures. @@ -256,6 +259,7 @@ See the following (incomplete) example: echo "Distro is ArchLinux" elif detectOs(Debian): echo "Distro is Debian" + ``` Uniform Syntax @@ -277,24 +281,24 @@ making it ideal for functional scripting metaprogramming. This is an example of a third party module that uses macros and templates to translate text strings on unmodified NimScript: -.. code-block:: nim - + ```nim import nimterlingua nimterlingua("translations.cfg") echo "cat" # Run with -d:RU becomes "kot", -d:ES becomes "gato", ... + ``` translations.cfg -.. code-block:: none - + ```none [cat] ES = gato IT = gatto RU = kot FR = chat + ``` -* `Nimterlingua `_ +* [Nimterlingua](https://nimble.directory/pkg/nimterlingua) Graceful Fallback @@ -305,8 +309,7 @@ but often a graceful and seamless fallback degradation is used. See the following NimScript: -.. code-block:: nim - + ```nim if likely(true): discard elif unlikely(false): @@ -316,6 +319,7 @@ See the following NimScript: static: echo CompileDate + ``` `likely()`, `unlikely()`, `static:` and `{.compiletime.}` @@ -326,14 +330,15 @@ Evolving Scripting language --------------------------- NimScript evolves together with Nim, -`occasionally new features might become available on NimScript `_ , +[occasionally new features might become available on NimScript]( +https://github.com/nim-lang/Nim/pulls?utf8=%E2%9C%93&q=nimscript), adapted from compiled Nim or added as new features on both. Scripting Language with a Package Manager ----------------------------------------- You can create your own modules to be compatible with NimScript, -and check `Nimble `_ +and check [Nimble](https://nimble.directory) to search for third party modules that may work on NimScript. DevOps Scripting @@ -342,4 +347,5 @@ DevOps Scripting You can use NimScript to deploy to production, run tests, build projects, do benchmarks, generate documentation, and all kinds of DevOps/SysAdmin specific tasks. -* `An example of a third party NimScript that can be used as a project-agnostic tool. `_ +* [An example of a third party NimScript that can be used as a project-agnostic + tool.](https://github.com/kaushalmodi/nim_config#list-available-tasks) diff --git a/doc/nimsuggest.rst b/doc/nimsuggest.md similarity index 91% rename from doc/nimsuggest.rst rename to doc/nimsuggest.md index 82693da0eb10..97cb2b1fabbc 100644 --- a/doc/nimsuggest.rst +++ b/doc/nimsuggest.md @@ -20,7 +20,7 @@ definition of symbols or suggestions for completion. This document will guide you through the available options. If you want to look at practical examples of nimsuggest support you can look at the -`various editor integrations `_ +[various editor integrations](https://github.com/Araq/Nim/wiki/Editor-Support) already available. @@ -29,8 +29,9 @@ Installation Nimsuggest is part of Nim's core. Build it via: -.. code:: cmd + ```cmd koch nimsuggest + ``` Nimsuggest invocation @@ -48,7 +49,7 @@ via sockets is more reasonable so that is the default. It listens to port 6000 by default. Nimsuggest is basically a frontend for the nim compiler so `--path`:option: flags and -`config files `_ +[config files](https://nim-lang.org/docs/nimc.html#compiler-usage-configuration-files) can be used to specify additional dependencies like `nimsuggest --stdin --debug --path:"dependencies" myproject.nim`:cmd:. @@ -62,10 +63,10 @@ a location. A query location consists of: ``file.nim`` - This is the name of the module or include file the query refers to. +: This is the name of the module or include file the query refers to. ``dirtyfile.nim`` - This is optional. +: This is optional. The `file` parameter is enough for static analysis, but IDEs tend to have *unsaved buffers* where the user may still be in @@ -76,11 +77,11 @@ a location. A query location consists of: ``line`` - An integer with the line you are going to query. For the compiler +: An integer with the line you are going to query. For the compiler lines start at **1**. ``col`` - An integer with the column you are going to query. For the +: An integer with the column you are going to query. For the compiler columns start at **0**. @@ -112,8 +113,8 @@ The `sug` Nimsuggest command performs a query about possible completion symbols at some point in the file. The typical usage scenario for this option is to call it after the -user has typed the dot character for `the object oriented call -syntax `_. +user has typed the dot character for [the object oriented call +syntax](tut2.html#object-oriented-programming-method-call-syntax). Nimsuggest will try to return the suggestions sorted first by scope (from innermost to outermost) and then by item name. diff --git a/doc/overview.rst b/doc/overview.md similarity index 86% rename from doc/overview.rst rename to doc/overview.md index e01520d7c82d..b21eb1e689e8 100644 --- a/doc/overview.rst +++ b/doc/overview.md @@ -5,5 +5,5 @@ Nim Documentation Overview :Author: Andreas Rumpf :Version: |nimversion| -.. include:: docs.rst +.. include:: docs.md diff --git a/doc/packaging.rst b/doc/packaging.md similarity index 67% rename from doc/packaging.rst rename to doc/packaging.md index 7976dfe92fc3..9a1cc0f6eb35 100644 --- a/doc/packaging.rst +++ b/doc/packaging.md @@ -4,9 +4,9 @@ Packaging Nim This page provide hints on distributing Nim using OS packages. -See `distros `_ for tools to detect Linux distribution at runtime. +See [distros](distros.html) for tools to detect Linux distribution at runtime. -See `here `_ for how to +See [here](intern.html#bootstrapping-the-compiler-reproducible-builds) for how to compile reproducible builds. Supported architectures @@ -48,24 +48,24 @@ The Debian package ships bash and ksh completion and manpages that can be reused Hints on the build process: -.. code:: cmd + ```cmd + # build from C sources and then using koch + make -j # supports parallel build + # alternatively: ./build.sh --os $os_type --cpu $cpu_arch + ./bin/nim c -d:release koch + ./koch boot -d:release - # build from C sources and then using koch - make -j # supports parallel build - # alternatively: ./build.sh --os $os_type --cpu $cpu_arch - ./bin/nim c -d:release koch - ./koch boot -d:release + # optionally generate docs into doc/html + ./koch docs - # optionally generate docs into doc/html - ./koch docs + ./koch tools - ./koch tools + # extract files to be really installed + ./install.sh - # extract files to be really installed - ./install.sh - - # also include the tools - for fn in nimble nimsuggest nimgrep; do cp ./bin/$fn /nim/bin/; done + # also include the tools + for fn in nimble nimsuggest nimgrep; do cp ./bin/$fn /nim/bin/; done + ``` What to install: diff --git a/doc/pegdocs.txt b/doc/pegdocs.txt index 0363d4874673..99d9d784cf9f 100644 --- a/doc/pegdocs.txt +++ b/doc/pegdocs.txt @@ -13,12 +13,12 @@ notation meaning ``A / ... / Z`` Ordered choice: Apply expressions `A`, ..., `Z`, in this order, to the text ahead, until one of them succeeds and possibly consumes some text. Indicate success if one of - expressions succeeded. Otherwise do not consume any text + expressions succeeded. Otherwise, do not consume any text and indicate failure. ``A ... Z`` Sequence: Apply expressions `A`, ..., `Z`, in this order, to consume consecutive portions of the text ahead, as long as they succeed. Indicate success if all succeeded. - Otherwise do not consume any text and indicate failure. + Otherwise, do not consume any text and indicate failure. The sequence's precedence is higher than that of ordered choice: ``A B / C`` means ``(A B) / Z`` and not ``A (B / Z)``. @@ -44,20 +44,20 @@ notation meaning ``E+`` One or more: Apply expression `E` repeatedly to match the text ahead, as long as it succeeds. Consume the matched text (if any) and indicate success if there was at least - one match. Otherwise indicate failure. + one match. Otherwise, indicate failure. ``E*`` Zero or more: Apply expression `E` repeatedly to match the text ahead, as long as it succeeds. Consume the matched text (if any). Always indicate success. ``E?`` Zero or one: If expression `E` matches the text ahead, consume it. Always indicate success. ``[s]`` Character class: If the character ahead appears in the - string `s`, consume it and indicate success. Otherwise + string `s`, consume it and indicate success. Otherwise, indicate failure. ``[a-b]`` Character range: If the character ahead is one from the range `a` through `b`, consume it and indicate success. - Otherwise indicate failure. + Otherwise, indicate failure. ``'s'`` String: If the text ahead is the string `s`, consume it - and indicate success. Otherwise indicate failure. + and indicate success. Otherwise, indicate failure. ``i's'`` String match ignoring case. ``y's'`` String match ignoring style. ``v's'`` Verbatim string match: Use this to override a global @@ -66,15 +66,15 @@ notation meaning ``y$j`` String match ignoring style for back reference. ``v$j`` Verbatim string match for back reference. ``.`` Any character: If there is a character ahead, consume it - and indicate success. Otherwise (that is, at the end of + and indicate success. Otherwise, (that is, at the end of input) indicate failure. -``_`` Any Unicode character: If there is an UTF-8 character - ahead, consume it and indicate success. Otherwise indicate +``_`` Any Unicode character: If there is a UTF-8 character + ahead, consume it and indicate success. Otherwise, indicate failure. ``@E`` Search: Shorthand for ``(!E .)* E``. (Search loop for the pattern `E`.) ``{@} E`` Captured Search: Shorthand for ``{(!E .)*} E``. (Search - loop for the pattern `E`.) Everything until and exluding + loop for the pattern `E`.) Everything until and excluding `E` is captured. ``@@ E`` Same as ``{@} E``. ``A <- E`` Rule: Bind the expression `E` to the *nonterminal symbol* @@ -82,7 +82,7 @@ notation meaning matching engine.** ``\identifier`` Built-in macro for a longer expression. ``\ddd`` Character with decimal code *ddd*. -``\"``, etc Literal ``"``, etc. +``\"``, etc. Literal ``"``, etc. =============== ============================================================ @@ -175,8 +175,9 @@ The PEG parser implements this grammar (written in PEG syntax):: expression, identifiers are not interpreted as non-terminals, but are interpreted as verbatim string: -.. code-block:: nim + ```nim abc =~ peg"abc" # is true + ``` So it is not necessary to write ``peg" 'abc' "`` in the above example. @@ -186,22 +187,25 @@ Examples Check if `s` matches Nim's "while" keyword: -.. code-block:: nim + ```nim s =~ peg" y'while'" + ``` Exchange (key, val)-pairs: -.. code-block:: nim + ```nim "key: val; key2: val2".replacef(peg"{\ident} \s* ':' \s* {\ident}", "$2: $1") + ``` Determine the ``#include``'ed files of a C file: -.. code-block:: nim + ```nim for line in lines("myfile.c"): if line =~ peg"""s <- ws '#include' ws '"' {[^"]+} '"' ws comment <- '/*' @ '*/' / '//' .* ws <- (comment / \s+)* """: echo matches[0] + ``` PEG vs regular expression ------------------------- diff --git a/doc/readme.txt b/doc/readme.txt index 6f4cece87772..7b1a445b50c1 100644 --- a/doc/readme.txt +++ b/doc/readme.txt @@ -1,5 +1,5 @@ ============================ -Nim's documenation system +Nim's documentation system ============================ This folder contains Nim's documentation. The documentation diff --git a/doc/refc.rst b/doc/refc.md similarity index 97% rename from doc/refc.rst rename to doc/refc.md index 766097f23b8b..6e4672ac5ff0 100644 --- a/doc/refc.rst +++ b/doc/refc.md @@ -16,9 +16,10 @@ defined via `--define:useRealtimeGC`:option: (you can put this into your config file as well). With this switch the garbage collector supports the following operations: -.. code-block:: nim + ```nim proc GC_setMaxPause*(maxPauseInUs: int) proc GC_step*(us: int, strongAdvice = false, stackSize = -1) + ``` The unit of the parameters `maxPauseInUs` and `us` is microseconds. @@ -98,9 +99,9 @@ As long as you don't use the threadvar emulation Nim uses native thread variables, of which you get a fresh version whenever you create a thread. You can then attach a GC to this thread via -.. code-block:: nim - + ```nim system.setupForeignThreadGc() + ``` It is **not** safe to disable the garbage collector and enable it after the call from your background thread even if the code you are calling is short @@ -109,10 +110,9 @@ lived. Before the thread exits, you should tear down the thread's GC to prevent memory leaks by calling -.. code-block:: nim - + ```nim system.tearDownForeignThreadGc() - + ``` Keeping track of memory @@ -121,7 +121,7 @@ Keeping track of memory If you need to pass around memory allocated by Nim to C, you can use the procs `GC_ref` and `GC_unref` to mark objects as referenced to avoid them being freed by the garbage collector. -Other useful procs from `system `_ you can use to keep track of memory are: +Other useful procs from [system](system.html) you can use to keep track of memory are: * `getTotalMem()` Returns the amount of total memory managed by the garbage collector. * `getOccupiedMem()` Bytes reserved by the garbage collector and used by objects. diff --git a/doc/sets_fragment.txt b/doc/sets_fragment.txt index d2e1f96f65ef..4aac75191fa5 100644 --- a/doc/sets_fragment.txt +++ b/doc/sets_fragment.txt @@ -13,18 +13,22 @@ range `0 .. MaxSetElements-1` where `MaxSetElements` is currently always The reason is that sets are implemented as high performance bit vectors. Attempting to declare a set with a larger type will result in an error: -.. code-block:: nim +```nim - var s: set[int64] # Error: set is too large + var s: set[int64] # Error: set is too large; use `std/sets` for ordinal types + # with more than 2^16 elements -**Note:** Nim also offers `hash sets `_ (which you need to import +``` + + +**Note:** Nim also offers [hash sets](sets.html) (which you need to import with `import sets`), which have no such restrictions. Sets can be constructed via the set constructor: `{}` is the empty set. The empty set is type compatible with any concrete set type. The constructor can also be used to include elements (and ranges of elements): -.. code-block:: nim + ```nim type CharSet = set[char] var @@ -32,6 +36,7 @@ can also be used to include elements (and ranges of elements): x = {'a'..'z', '0'..'9'} # This constructs a set that contains the # letters from 'a' to 'z' and the digits # from '0' to '9' + ``` These operations are supported by sets: @@ -52,8 +57,7 @@ operation meaning `excl(A, elem)` same as `A = A - {elem}` ================== ======================================================== -Bit fields -~~~~~~~~~~ +### Bit fields Sets are often used to define a type for the *flags* of a procedure. This is a cleaner (and type safe) solution than defining integer @@ -61,8 +65,7 @@ constants that have to be `or`'ed together. Enum, sets and casting can be used together as in: -.. code-block:: nim - + ```nim type MyFlag* {.size: sizeof(cint).} = enum A @@ -80,10 +83,11 @@ Enum, sets and casting can be used together as in: assert toNum({A, C}) == 5 assert toFlags(0) == {} assert toFlags(7) == {A, B, C} + ``` Note how the set turns enum values into powers of 2. If using enums and sets with C, use distinct cint. For interoperability with C see also the -`bitsize pragma `_. +[bitsize pragma](manual.html#implementation-specific-pragmas-bitsize-pragma). diff --git a/doc/spawn.txt b/doc/spawn.txt index c437e8aa3241..ed25ad5fdddd 100644 --- a/doc/spawn.txt +++ b/doc/spawn.txt @@ -6,7 +6,7 @@ Nim has two flavors of parallelism: 1) `Structured`:idx parallelism via the ``parallel`` statement. 2) `Unstructured`:idx: parallelism via the standalone ``spawn`` statement. -Both need the `threadpool `_ module to work. +Both need the [threadpool](threadpool.html) module to work. Somewhat confusingly, ``spawn`` is also used in the ``parallel`` statement with slightly different semantics. ``spawn`` always takes a call expression of @@ -28,7 +28,7 @@ the passed expression on the thread pool and returns a `data flow variable`:idx: **blocking**. However, one can use ``blockUntilAny`` to wait on multiple flow variables at the same time: -.. code-block:: nim + ```nim import std/threadpool, ... # wait until 2 out of 3 servers received the update: @@ -40,6 +40,7 @@ variables at the same time: assert index >= 0 responses.del(index) discard blockUntilAny(responses) + ``` Data flow variables ensure that no data races are possible. Due to technical limitations not every type ``T`` is possible in @@ -54,7 +55,7 @@ Parallel statement Example: -.. code-block:: nim + ```nim # Compute PI in an inefficient way import std/[strutils, math, threadpool] @@ -69,6 +70,7 @@ Example: result += ch[k] echo formatFloat(pi(5000)) + ``` The parallel statement is the preferred mechanism to introduce parallelism diff --git a/doc/subexes.txt b/doc/subexes.txt index 62ddd1ec8f8f..1bfd602131c2 100644 --- a/doc/subexes.txt +++ b/doc/subexes.txt @@ -47,8 +47,7 @@ Notation meaning Examples ======== -.. code-block:: nim - + ```nim subex"$1($', '{2..})" % ["f", "a", "b", "c"] == "f(a, b, c)" subex"$1 $[files|file|files]{1} copied" % ["1"] == "1 file copied" @@ -57,5 +56,5 @@ Examples subex("type\n TEnum = enum\n $', '40c'\n '{..}") % [ "fieldNameA", "fieldNameB", "fieldNameC", "fieldNameD"] - + ``` diff --git a/doc/testament.rst b/doc/testament.md similarity index 67% rename from doc/testament.rst rename to doc/testament.md index 5590cc6d79f6..812b109849fd 100644 --- a/doc/testament.rst +++ b/doc/testament.md @@ -8,8 +8,8 @@ Testament is an advanced automatic unittests runner for Nim tests, is used for the development of Nim itself, offers process isolation for your tests, it can generate statistics about test cases, -supports multiple targets (C, C++, ObjectiveC, JavaScript, etc), -simulated `Dry-Runs `_, +supports multiple targets (C, C++, ObjectiveC, JavaScript, etc.), +simulated [Dry-Runs](https://en.wikipedia.org/wiki/Dry_run_(testing)), has logging, can generate HTML reports, skip tests from a file, and more, so can be useful to run your tests, even the most complex ones. @@ -17,7 +17,7 @@ so can be useful to run your tests, even the most complex ones. Test files location =================== -By default Testament looks for test files on ``"./tests/*.nim"``. +By default, Testament looks for test files on ``"./tests/*.nim"``. You can overwrite this pattern glob using `pattern `:option:. The default working directory path can be changed using `--directory:"folder/subfolder/"`:option:. @@ -36,13 +36,13 @@ Options (for debugging) --failing Only show failing/ignored tests --targets:"c cpp js objc" - Run tests for specified targets (default: all) + Run tests for specified targets (default: c) --nim:path Use a particular nim executable (default: $PATH/nim) --directory:dir Change to directory dir before reading the tests or doing anything else. --colors:on|off Turn messages coloring on|off. --backendLogging:on|off Disable or enable backend logging. - By default turned on. + By default, turned on. --skipFrom:file Read tests to skip from ``file`` - one test per line, # comments ignored @@ -53,29 +53,29 @@ Running a single test This is a minimal example to understand the basics, not very useful for production, but easy to understand: -.. code:: console - + ```console $ mkdir tests $ echo "assert 42 == 42" > tests/test0.nim $ testament run test0.nim PASS: tests/test0.nim C ( 0.2 sec) $ testament r test0 PASS: tests/test0.nim C ( 0.2 sec) + ``` Running all tests from a directory ================================== -.. code:: console - + ```console $ testament pattern "tests/*.nim" + ``` To search for tests deeper in a directory, use -.. code:: console - + ```console $ testament pattern "tests/**/*.nim" # one level deeper $ testament pattern "tests/**/**/*.nim" # two levels deeper + ``` HTML Reports ============ @@ -83,18 +83,17 @@ HTML Reports Generate HTML Reports ``testresults.html`` from unittests, you have to run at least 1 test *before* generating a report: -.. code:: console - + ```console $ testament html + ``` -Writing Unitests -================ +Writing Unit tests +================== Example "template" **to edit** and write a Testament unittest: -.. code-block:: nim - + ```nim discard """ # What actions to expect completion on. @@ -112,9 +111,9 @@ Example "template" **to edit** and write a Testament unittest: exitcode: 0 # Provide an `output` string to assert that the test prints to standard out - # exatly the expected string. Provide an `outputsub` string to assert that + # exactly the expected string. Provide an `outputsub` string to assert that # the string given here is a substring of the standard out output of the - # test. + # test (the output includes both the compiler and test execution output). output: "" outputsub: "" @@ -139,7 +138,7 @@ Example "template" **to edit** and write a Testament unittest: # Can be run in batch mode, or not. batchable: true - # Can be run Joined with other tests to run all togheter, or not. + # Can be run Joined with other tests to run all together, or not. joinable: true # On Linux 64-bit machines, whether to use Valgrind to check for bad memory @@ -154,8 +153,7 @@ Example "template" **to edit** and write a Testament unittest: # Command the test should use to run. If left out or an empty string is # provided, the command is taken to be: # "nim $target --hints:on -d:testing --nimblePath:build/deps/pkgs $options $file" - # You can use the $target, $options, and $file placeholders in your own - # command, too. + # Subject to variable interpolation. cmd: "nim c -r $file" # Maximum generated temporary intermediate code file size for the test. @@ -164,7 +162,7 @@ Example "template" **to edit** and write a Testament unittest: # Timeout seconds to run the test. Fractional values are supported. timeout: 1.5 - # Targets to run the test into (c, cpp, objc, js). + # Targets to run the test into (c, cpp, objc, js). Defaults to c. targets: "c js" # flags with which to run the test, delimited by `;` @@ -182,41 +180,111 @@ Example "template" **to edit** and write a Testament unittest: """ assert true assert 42 == 42, "Assert error message" + ``` * As you can see the "Spec" is just a `discard """ """`. * Spec has sane defaults, so you don't need to provide them all, any simple assert will work just fine. -* `This is not the full spec of Testament, check the Testament Spec on GitHub, see parseSpec(). `_ -* `Nim itself uses Testament, so there are plenty of test examples. `_ +* This is not the full spec of Testament, check [the Testament Spec on GitHub, + see parseSpec()](https://github.com/nim-lang/Nim/blob/devel/testament/specs.nim#L315). +* Nim itself uses Testament, so [there are plenty of test examples]( + https://github.com/nim-lang/Nim/tree/devel/tests). * Has some built-in CI compatibility, like Azure Pipelines, etc. -* `Testament supports inlined error messages on Unittests, basically comments with the expected error directly on the code. `_ -Unitests Examples -================= +Inline hints, warnings and errors (notes) +----------------------------------------- -Expected to fail: +Testing the line, column, kind and message of hints, warnings and errors can +be written inline like so: + ```nim + {.warning: "warning!!"} #[tt.Warning + ^ warning!! [User] ]# + ``` + +The opening `#[tt.` marks the message line. +The `^` marks the message column. + +Inline messages can be combined with `nimout` when `nimoutFull` is false (default). +This allows testing for expected messages from other modules: + + ```nim + discard """ + nimout: "config.nims(1, 1) Hint: some hint message [User]" + """ + {.warning: "warning!!"} #[tt.Warning + ^ warning!! [User] ]# + ``` + +Multiple messages for a line can be checked by delimiting messages with ';': + + ```nim + discard """ + matrix: "--errorMax:0 --styleCheck:error" + """ + + proc generic_proc*[T](a_a: int) = #[tt.Error + ^ 'generic_proc' should be: 'genericProc'; tt.Error + ^ 'a_a' should be: 'aA' ]# + discard + ``` + +Use `--errorMax:0` in `matrix`, or `cmd: "nim check $file"` when testing +for multiple 'Error' messages. + +Output message variable interpolation +------------------------------------- + +`errormsg`, `nimout`, and inline messages are subject to these variable interpolations: + +* `${/}` - platform's directory separator +* `$file` - the filename (without directory) of the test + +All other `$` characters need escaped as `$$`. + +Cmd variable interpolation +-------------------------- + +The `cmd` option is subject to these variable interpolations: + +* `$target` - the compilation target, e.g. `c`. +* `$options` - the options for the compiler. +* `$file` - the file path of the test. +* `$filedir` - the directory of the test file. .. code-block:: nim + discard """ + cmd: "nim $target --nimblePath:./nimbleDir/simplePkgs $options $file" + """ + +All other `$` characters need escaped as `$$`. + + +Unit test Examples +================== + +Expected to fail: + + ```nim discard """ errormsg: "undeclared identifier: 'not_defined'" """ assert not_defined == "not_defined", "not_defined is not defined" + ``` Non-Zero exit code: -.. code-block:: nim - + ```nim discard """ exitcode: 1 """ quit "Non-Zero exit code", 1 + ``` Standard output checking: -.. code-block:: nim - + ```nim discard """ output: ''' @@ -230,33 +298,34 @@ Standard output checking: """ for i in 0..5: echo i + ``` JavaScript tests: -.. code-block:: nim - + ```nim discard """ targets: "js" """ when defined(js): import std/jsconsole console.log("My Frontend Project") + ``` Compile-time tests: -.. code-block:: nim - + ```nim discard """ action: "compile" """ static: assert 9 == 9, "Compile time assert" + ``` Tests without Spec: -.. code-block:: nim - + ```nim assert 1 == 1 + ``` See also: -* `Unittest `_ +* [Unittest](unittest.html) diff --git a/doc/tools.rst b/doc/tools.md similarity index 75% rename from doc/tools.rst rename to doc/tools.md index 0de4ac914321..6849103f9e9e 100644 --- a/doc/tools.rst +++ b/doc/tools.md @@ -7,36 +7,36 @@ Tools available with Nim The standard distribution ships with the following tools: -- | `Hot code reloading `_ +- | [Hot code reloading](hcr.html) | The "Hot code reloading" feature is built into the compiler but has its own document explaining how it works. -- | `Documentation generator `_ +- | [Documentation generator](docgen.html) | The builtin document generator `nim doc`:cmd: generates HTML documentation from ``.nim`` source files. -- | `Nimsuggest for IDE support `_ +- | [Nimsuggest for IDE support](nimsuggest.html) | Through the `nimsuggest`:cmd: tool, any IDE can query a ``.nim`` source file and obtain useful information like the definition of symbols or suggestions for completion. -- | `C2nim `_ +- | [C2nim](https://github.com/nim-lang/c2nim/blob/master/doc/c2nim.rst) | C to Nim source converter. Translates C header files to Nim. -- | `niminst `_ +- | [niminst](niminst.html) | niminst is a tool to generate an installer for a Nim program. -- | `nimgrep `_ +- | [nimgrep](nimgrep.html) | Nim search and replace utility. - | nimpretty | `nimpretty`:cmd: is a Nim source code beautifier, to format code according to the official style guide. -- | `testament `_ +- | [testament](https://nim-lang.github.io/Nim/testament.html) | `testament`:cmd: is an advanced automatic *unittests runner* for Nim tests, is used for the development of Nim itself, offers process isolation for your tests, it can generate statistics about test cases, supports multiple targets (C, JS, etc), - `simulated Dry-Runs `_, + [simulated Dry-Runs](https://en.wikipedia.org/wiki/Dry_run_(testing)), has logging, can generate HTML reports, skip tests from a file, and more, so can be useful to run your tests, even the most complex ones. diff --git a/doc/tut1.rst b/doc/tut1.md similarity index 86% rename from doc/tut1.rst rename to doc/tut1.md index 405df57b1ddc..c66ff7337114 100644 --- a/doc/tut1.rst +++ b/doc/tut1.md @@ -12,10 +12,7 @@ Nim Tutorial (Part I) Introduction ============ -.. raw:: html -

    - "Der Mensch ist doch ein Augentier -- schöne Dinge wünsch ich mir." -

    +> "Der Mensch ist doch ein Augentier -- Schöne Dinge wünsch' ich mir." This document is a tutorial for the programming language *Nim*. @@ -25,14 +22,14 @@ like variables, types, or statements. Here are several other resources for learning Nim: -* `Nim Basics tutorial `_ - a gentle +* [Nim Basics tutorial](https://narimiran.github.io/nim-basics/) - a gentle introduction of the concepts mentioned above -* `Learn Nim in 5 minutes `_ - quick, +* [Learn Nim in 5 minutes](https://learnxinyminutes.com/docs/nim/) - quick, five-minute introduction to Nim -* `The Nim manual `_ - many more examples of the advanced language features +* [The Nim manual](manual.html) - many more examples of the advanced language features All code examples in this tutorial, as well as the ones found in the rest of -Nim's documentation, follow the `Nim style guide `_. +Nim's documentation, follow the [Nim style guide](nep1.html). The first program @@ -40,19 +37,19 @@ The first program We start the tour with a modified "hello world" program: -.. code-block:: Nim - :test: "nim c $1" + ```Nim test = "nim c $1" # This is a comment echo "What's your name? " var name: string = readLine(stdin) echo "Hi, ", name, "!" + ``` Save this code to the file "greetings.nim". Now compile and run it:: nim compile --run greetings.nim -With the ``--run`` `switch `_ Nim +With the ``--run`` [switch](nimc.html#compiler-usage-commandminusline-switches) Nim executes the file automatically after compilation. You can give your program command-line arguments by appending them after the filename:: @@ -69,12 +66,12 @@ To compile a release version use:: By default, the Nim compiler generates a large number of runtime checks aiming for your debugging pleasure. With ``-d:release`` some checks are -`turned off and optimizations are turned on -`_. +[turned off and optimizations are turned on]( +nimc.html#compiler-usage-compileminustime-symbols). For benchmarking or production code, use the ``-d:release`` switch. For comparing the performance with unsafe languages like C, use the ``-d:danger`` switch -in order to get meaningful, comparable results. Otherwise Nim might be handicapped +in order to get meaningful, comparable results. Otherwise, Nim might be handicapped by checks that are **not even available** for C. Though it should be pretty obvious what the program does, I will explain the @@ -84,21 +81,21 @@ done with spaces only, tabulators are not allowed. String literals are enclosed in double-quotes. The `var` statement declares a new variable named `name` of type `string` with the value that is -returned by the `readLine `_ procedure. Since the -compiler knows that `readLine `_ returns a string, +returned by the [readLine](syncio.html#readLine,File) procedure. Since the +compiler knows that [readLine](syncio.html#readLine,File) returns a string, you can leave out the type in the declaration (this is called `local type inference`:idx:). So this will work too: -.. code-block:: Nim - :test: "nim c $1" + ```Nim test = "nim c $1" var name = readLine(stdin) + ``` Note that this is basically the only form of type inference that exists in Nim: it is a good compromise between brevity and readability. The "hello world" program contains several identifiers that are already known -to the compiler: `echo`, `readLine `_, etc. -These built-ins are declared in the system_ module which is implicitly +to the compiler: `echo`, [readLine](syncio.html#readLine,File), etc. +These built-ins are declared in the [system](system.html) module which is implicitly imported by any other module. @@ -117,8 +114,9 @@ String literals are enclosed in double-quotes; character literals in single quotes. Special characters are escaped with ``\``: ``\n`` means newline, ``\t`` means tabulator, etc. There are also *raw* string literals: -.. code-block:: Nim + ```Nim r"C:\program files\nim" + ``` In raw literals, the backslash is not an escape character. @@ -134,11 +132,11 @@ Comments Comments start anywhere outside a string or character literal with the hash character `#`. Documentation comments start with `##`: -.. code-block:: nim - :test: "nim c $1" + ```nim test = "nim c $1" # A comment. var myVariable: int ## a documentation comment + ``` Documentation comments are tokens; they are only allowed at certain places in @@ -148,8 +146,7 @@ documentation generators. Multiline comments are started with `#[` and terminated with `]#`. Multiline comments can also be nested. -.. code-block:: nim - :test: "nim c $1" + ```nim test = "nim c $1" #[ You can have any Nim code text commented out inside this with no indentation restrictions. @@ -158,6 +155,7 @@ comments can also be nested. Note: these can be nested!! ]# ]# + ``` Numbers @@ -175,18 +173,19 @@ The var statement ================= The var statement declares a new local or global variable: -.. code-block:: + ```nim var x, y: int # declares x and y to have the type `int` + ``` Indentation can be used after the `var` keyword to list a whole section of variables: -.. code-block:: - :test: "nim c $1" + ```nim test = "nim c $1" var x, y: int # a comment can occur here too a, b, c: string + ``` Constants @@ -196,20 +195,20 @@ Constants are symbols which are bound to a value. The constant's value cannot change. The compiler must be able to evaluate the expression in a constant declaration at compile time: -.. code-block:: nim - :test: "nim c $1" + ```nim test = "nim c $1" const x = "abc" # the constant x contains the string "abc" + ``` Indentation can be used after the `const` keyword to list a whole section of constants: -.. code-block:: - :test: "nim c $1" + ```nim test = "nim c $1" const x = 1 # a comment can occur here too y = 2 z = y + 5 # computations are possible + ``` The let statement @@ -218,20 +217,22 @@ The `let` statement works like the `var` statement but the declared symbols are *single assignment* variables: After the initialization their value cannot change: -.. code-block:: + ```nim let x = "abc" # introduces a new variable `x` and binds a value to it x = "xyz" # Illegal: assignment to `x` + ``` The difference between `let` and `const` is: `let` introduces a variable that can not be re-assigned, `const` means "enforce compile time evaluation and put it into a data section": -.. code-block:: + ```nim const input = readLine(stdin) # Error: constant expression expected + ``` -.. code-block:: - :test: "nim c $1" + ```nim test = "nim c $1" let input = readLine(stdin) # works + ``` The assignment statement @@ -240,22 +241,23 @@ The assignment statement The assignment statement assigns a new value to a variable or more generally to a storage location: -.. code-block:: + ```nim var x = "abc" # introduces a new variable `x` and assigns a value to it x = "xyz" # assigns a new value to `x` + ``` `=` is the *assignment operator*. The assignment operator can be overloaded. You can declare multiple variables with a single assignment statement and all the variables will have the same value: -.. code-block:: - :test: "nim c $1" + ```nim test = "nim c $1" var x, y = 3 # assigns 3 to the variables `x` and `y` echo "x ", x # outputs "x 3" echo "y ", y # outputs "y 3" x = 42 # changes `x` to 42 without changing `y` echo "x ", x # outputs "x 42" echo "y ", y # outputs "y 3" + ``` Control flow statements @@ -271,8 +273,7 @@ If statement The if statement is one way to branch the control flow: -.. code-block:: nim - :test: "nim c $1" + ```nim test = "nim c $1" let name = readLine(stdin) if name == "": echo "Poor soul, you lost your name?" @@ -280,6 +281,7 @@ The if statement is one way to branch the control flow: echo "Very funny, your name is name." else: echo "Hi, ", name, "!" + ``` There can be zero or more `elif` parts, and the `else` part is optional. The keyword `elif` is short for `else if`, and is useful to avoid @@ -290,11 +292,10 @@ characters.) Case statement -------------- -Another way to branch is provided by the case statement. A case statement is -a multi-branch: +Another way to branch is provided by the case statement. A case statement allows +for multiple branches: -.. code-block:: nim - :test: "nim c $1" + ```nim test = "nim c $1" let name = readLine(stdin) case name of "": @@ -305,6 +306,7 @@ a multi-branch: echo "Cool name!" else: echo "Hi, ", name, "!" + ``` As it can be seen, for an `of` branch a comma-separated list of values is also allowed. @@ -313,7 +315,7 @@ The case statement can deal with integers, other ordinal types, and strings. (What an ordinal type is will be explained soon.) For integers or other ordinal types value ranges are also possible: -.. code-block:: nim + ```nim # this statement will be explained later: from std/strutils import parseInt @@ -322,6 +324,7 @@ For integers or other ordinal types value ranges are also possible: case n of 0..2, 4..7: echo "The number is in the set: {0, 1, 2, 4, 5, 6, 7}" of 3, 8: echo "The number is 3 or 8" + ``` However, the above code **does not compile**: the reason is that you have to cover every value that `n` may contain, but the code only handles the values @@ -329,14 +332,15 @@ every value that `n` may contain, but the code only handles the values (though it is possible thanks to the range notation), we fix this by telling the compiler that for every other value nothing should be done: -.. code-block:: nim + ```nim ... case n of 0..2, 4..7: echo "The number is in the set: {0, 1, 2, 4, 5, 6, 7}" of 3, 8: echo "The number is 3 or 8" else: discard + ``` -The empty `discard statement <#procedures-discard-statement>`_ is a *do +The empty [discard statement] is a *do nothing* statement. The compiler knows that a case statement with an else part cannot fail and thus the error disappears. Note that it is impossible to cover all possible string values: that is why string cases always need an `else` @@ -352,14 +356,13 @@ While statement The while statement is a simple looping construct: -.. code-block:: nim - :test: "nim c $1" - + ```nim test = "nim c $1" echo "What's your name? " var name = readLine(stdin) while name == "": echo "Please tell me your name: " name = readLine(stdin) # no `var`, because we do not declare a new variable here + ``` The example uses a while loop to keep asking the users for their name, as long as the user types in nothing (only presses RETURN). @@ -369,76 +372,82 @@ For statement ------------- The `for` statement is a construct to loop over any element an *iterator* -provides. The example uses the built-in `countup -`_ iterator: +provides. The example uses the built-in [countup]( +system.html#countup.i,T,T,Positive) iterator: -.. code-block:: nim - :test: "nim c $1" + ```nim test = "nim c $1" echo "Counting to ten: " for i in countup(1, 10): echo i # --> Outputs 1 2 3 4 5 6 7 8 9 10 on different lines + ``` The variable `i` is implicitly declared by the -`for` loop and has the type `int`, because that is what `countup -`_ returns. `i` runs through the values +`for` loop and has the type `int`, because that is what [countup]( +system.html#countup.i,T,T,Positive) returns. `i` runs through the values 1, 2, .., 10. Each value is `echo`-ed. This code does the same: -.. code-block:: nim + ```nim echo "Counting to 10: " var i = 1 while i <= 10: echo i inc i # increment i by 1 # --> Outputs 1 2 3 4 5 6 7 8 9 10 on different lines + ``` -Since counting up occurs so often in programs, Nim also has a `.. -`_ iterator that does the same: +Since counting up occurs so often in programs, Nim also has a [..]( +system.html#...i,T,T) iterator that does the same: -.. code-block:: nim + ```nim for i in 1 .. 10: ... + ``` Counting down can be achieved as easily (but is less often needed): -.. code-block:: nim + ```nim echo "Counting down from 10 to 1: " for i in countdown(10, 1): echo i # --> Outputs 10 9 8 7 6 5 4 3 2 1 on different lines + ``` Zero-indexed counting has two shortcuts `..<` and `.. ^1` -(`backward index operator `_) to simplify +([backward index operator](system.html#^.t%2Cint)) to simplify counting to one less than the higher index: -.. code-block:: nim + ```nim for i in 0 ..< 10: ... # the same as 0 .. 9 + ``` or -.. code-block:: nim + ```nim var s = "some string" for i in 0 ..< s.len: ... + ``` or -.. code-block:: nim + ```nim var s = "some string" for idx, c in s[0 .. ^1]: ... # ^1 is the last element, ^2 would be one before it, and so on + ``` Other useful iterators for collections (like arrays and sequences) are * `items` and `mitems`, which provides immutable and mutable elements respectively, and * `pairs` and `mpairs` which provides the element and an index number (immutable and mutable respectively) -.. code-block:: nim - :test: "nim c $1" + ```nim test = "nim c $1" for index, item in ["a","b"].pairs: echo item, " at index ", index # => a at index 0 # => b at index 1 + ``` Scopes and the block statement ------------------------------ @@ -447,23 +456,21 @@ Control flow statements have a feature not covered yet: they open a new scope. This means that in the following example, `x` is not accessible outside the loop: -.. code-block:: nim - :test: "nim c $1" - :status: 1 + ```nim test = "nim c $1" status = 1 while false: var x = "hi" echo x # does not work + ``` A while (for) statement introduces an implicit block. Identifiers are only visible within the block they have been declared. The `block` statement can be used to open a new block explicitly: -.. code-block:: nim - :test: "nim c $1" - :status: 1 + ```nim test = "nim c $1" status = 1 block myblock: var x = "hi" echo x # does not work either + ``` The block's *label* (`myblock` in the example) is optional. @@ -475,8 +482,7 @@ A block can be left prematurely with a `break` statement. The break statement can leave a `while`, `for`, or a `block` statement. It leaves the innermost construct, unless a label of a block is given: -.. code-block:: nim - :test: "nim c $1" + ```nim test = "nim c $1" block myblock: echo "entering block" while true: @@ -492,6 +498,7 @@ innermost construct, unless a label of a block is given: break myblock2 # leaves the block (and the loop) echo "still in block" # it won't be printed echo "outside the block" + ``` Continue statement @@ -500,11 +507,11 @@ Continue statement Like in many other programming languages, a `continue` statement starts the next iteration immediately: -.. code-block:: nim - :test: "nim c $1" + ```nim test = "nim c $1" for i in 1 .. 5: if i <= 3: continue echo i # will only print 4 and 5 + ``` When statement @@ -512,9 +519,7 @@ When statement Example: -.. code-block:: nim - :test: "nim c $1" - + ```nim test = "nim c $1" when system.hostOS == "windows": echo "running on Windows!" elif system.hostOS == "linux": @@ -523,6 +528,7 @@ Example: echo "running on Mac OS X!" else: echo "unknown operating system" + ``` The `when` statement is almost identical to the `if` statement, but with these differences: @@ -550,7 +556,7 @@ statements. *Complex statements* like `if`, `when`, `for`, `while` can contain other statements. To avoid ambiguities, complex statements must always be indented, but single simple statements do not: -.. code-block:: nim + ```nim # no indentation needed for single-assignment statement: if x: x = false @@ -565,18 +571,19 @@ be indented, but single simple statements do not: if x: x = false y = false + ``` *Expressions* are parts of a statement that usually result in a value. The condition in an if statement is an example of an expression. Expressions can contain indentation at certain places for better readability: -.. code-block:: nim - + ```nim if thisIsaLongCondition() and thisIsAnotherLongCondition(1, 2, 3, 4): x = true + ``` As a rule of thumb, indentation within expressions is allowed after operators, an open parenthesis and after commas. @@ -584,24 +591,23 @@ an open parenthesis and after commas. With parenthesis and semicolons `(;)` you can use statements where only an expression is allowed: -.. code-block:: nim - :test: "nim c $1" + ```nim test = "nim c $1" # computes fac(4) at compile time: const fac4 = (var x = 1; for i in 1..4: x *= i; x) + ``` Procedures ========== -To define new commands like `echo `_ -and `readLine `_ in the examples, the concept of a +To define new commands like [echo](system.html#echo,varargs[typed,]) +and [readLine](syncio.html#readLine,File) in the examples, the concept of a *procedure* is needed. You might be used to them being called *methods* or *functions* in other languages, but Nim -`differentiates these concepts `_. In +[differentiates these concepts](tut1.html#procedures-funcs-and-methods). In Nim, new procedures are defined with the `proc` keyword: -.. code-block:: nim - :test: "nim c $1" + ```nim test = "nim c $1" proc yes(question: string): bool = echo question, " (y/n)" while true: @@ -614,6 +620,7 @@ Nim, new procedures are defined with the `proc` keyword: echo "I'm sorry Dave, I'm afraid I can't do that." else: echo "I think you know what the problem is just as well as I do." + ``` This example shows a procedure named `yes` that asks the user a `question` and returns true if they answered "yes" (or something similar) and returns @@ -638,8 +645,7 @@ shorthand for `return result`. The `result` value is always returned automatically at the end of a procedure if there is no `return` statement at the exit. -.. code-block:: nim - :test: "nim c $1" + ```nim test = "nim c $1" proc sumTillNegative(x: varargs[int]): int = for i in x: if i < 0: @@ -649,6 +655,7 @@ the exit. echo sumTillNegative() # echoes 0 echo sumTillNegative(3, 4, 5) # echoes 12 echo sumTillNegative(3, 4 , -1 , 6) # echoes 7 + ``` The `result` variable is already implicitly declared at the start of the function, so declaring it again with 'var result', for example, would shadow it @@ -661,10 +668,10 @@ A procedure that does not have any `return` statement and does not use the special `result` variable returns the value of its last expression. For example, this procedure -.. code-block:: nim - :test: "nim c $1" + ```nim test = "nim c $1" proc helloWorld(): string = "Hello, World!" + ``` returns the string "Hello, World!". @@ -677,18 +684,17 @@ most efficient way. If a mutable variable is needed inside the procedure, it has to be declared with `var` in the procedure body. Shadowing the parameter name is possible, and actually an idiom: -.. code-block:: nim - :test: "nim c $1" + ```nim test = "nim c $1" proc printSeq(s: seq, nprinted: int = -1) = var nprinted = if nprinted == -1: s.len else: min(nprinted, s.len) for i in 0 ..< nprinted: echo s[i] + ``` If the procedure needs to modify the argument for the caller, a `var` parameter can be used: -.. code-block:: nim - :test: "nim c $1" + ```nim test = "nim c $1" proc divmod(a, b: int; res, remainder: var int) = res = a div b # integer division remainder = a mod b # integer modulo operation @@ -698,6 +704,7 @@ caller, a `var` parameter can be used: divmod(8, 5, x, y) # modifies x and y echo x echo y + ``` In the example, `res` and `remainder` are `var parameters`. Var parameters can be modified by the procedure and the changes are @@ -712,19 +719,20 @@ To call a procedure that returns a value just for its side effects and ignoring its return value, a `discard` statement **must** be used. Nim does not allow silently throwing away a return value: -.. code-block:: nim + ```nim discard yes("May I ask a pointless question?") + ``` The return value can be ignored implicitly if the called proc/iterator has been declared with the `discardable` pragma: -.. code-block:: nim - :test: "nim c $1" + ```nim test = "nim c $1" proc p(x, y: int): int {.discardable.} = return x + y p(3, 4) # now valid + ``` Named arguments @@ -732,24 +740,26 @@ Named arguments Often a procedure has many parameters and it is not clear in which order the parameters appear. This is especially true for procedures that construct a -complex data type. Therefore the arguments to a procedure can be named, so +complex data type. Therefore, the arguments to a procedure can be named, so that it is clear which argument belongs to which parameter: -.. code-block:: nim + ```nim proc createWindow(x, y, width, height: int; title: string; show: bool): Window = ... var w = createWindow(show = true, title = "My Application", x = 0, y = 0, height = 600, width = 800) + ``` Now that we use named arguments to call `createWindow` the argument order does not matter anymore. Mixing named arguments with ordered arguments is also possible, but not very readable: -.. code-block:: nim + ```nim var w = createWindow(0, 0, title = "My Application", height = 600, width = 800, true) + ``` The compiler checks that each parameter receives exactly one argument. @@ -761,13 +771,14 @@ To make the `createWindow` proc easier to use it should provide `default values`; these are values that are used as arguments if the caller does not specify them: -.. code-block:: nim + ```nim proc createWindow(x = 0, y = 0, width = 500, height = 700, title = "unknown", show = true): Window = ... var w = createWindow(title = "My Application", height = 600, width = 800) + ``` Now the call to `createWindow` only needs to set the values that differ from the defaults. @@ -781,7 +792,7 @@ Overloaded procedures Nim provides the ability to overload procedures similar to C++: -.. code-block:: nim + ```nim proc toString(x: int): string = result = if x < 0: "negative" @@ -795,8 +806,9 @@ Nim provides the ability to overload procedures similar to C++: assert toString(13) == "positive" # calls the toString(x: int) proc assert toString(true) == "yep" # calls the toString(x: bool) proc + ``` -(Note that `toString` is usually the `$ `_ operator in +(Note that `toString` is usually the [$](dollars.html) operator in Nim.) The compiler chooses the most appropriate proc for the `toString` calls. How this overloading resolution algorithm works exactly is not discussed here -- see the manual for details. Ambiguous calls are reported as errors. @@ -821,21 +833,22 @@ User-defined operators are allowed. Nothing stops you from defining your own `@!?+~` operator, but doing so may reduce readability. The operator's precedence is determined by its first character. The details -can be `found in the manual `_. +can be [found in the manual](manual.html#syntax-precedence). To define a new operator enclose the operator in backticks "`": -.. code-block:: nim + ```nim proc `$` (x: myDataType): string = ... # now the $ operator also works with myDataType, overloading resolution # ensures that $ works for built-in types just like before + ``` The "`" notation can also be used to call an operator just like any other procedure: -.. code-block:: nim - :test: "nim c $1" + ```nim test = "nim c $1" if `==`( `+`(3, 4), 7): echo "true" + ``` Forward declarations @@ -846,11 +859,12 @@ Every variable, procedure, etc. needs to be declared before it can be used. language that supports metaprogramming as extensively as Nim does.) However, this cannot be done for mutually recursive procedures: -.. code-block:: nim + ```nim # forward declaration: proc even(n: int): bool + ``` -.. code-block:: nim + ```nim proc odd(n: int): bool = assert(n >= 0) # makes sure we don't run into negative recursion if n == 0: false @@ -862,12 +876,13 @@ However, this cannot be done for mutually recursive procedures: if n == 1: false else: n == 0 or odd(n-1) + ``` Here `odd` depends on `even` and vice versa. Thus `even` needs to be introduced to the compiler before it is completely defined. The syntax for such a forward declaration is simple: just omit the `=` and the procedure's body. The `assert` just adds border conditions, and will be -covered later in `Modules`_ section. +covered later in [Modules] section. Later versions of the language will weaken the requirements for forward declarations. @@ -893,12 +908,12 @@ with `{.noSideEffects.}`. Functions can still change their mutable arguments however, which are those marked as `var`, along with any `ref` objects. Unlike procedures, methods are dynamically dispatched. This sounds a bit -complicated, but it is a concept closely related to inheritance and object oriented +complicated, but it is a concept closely related to inheritance and object-oriented programming. If you overload a procedure (two procedures with the same name but of different types or with different sets of arguments are said to be overloaded), the procedure to use is determined at compile-time. Methods, on the other hand, depend on objects that inherit from the `RootObj`. This is something that is covered in much greater depth in -the `second part of the tutorial`_. +the [second part of the tutorial](tut2.html#object-oriented-programming-dynamic-dispatch). Iterators @@ -906,21 +921,22 @@ Iterators Let's return to the simple counting example: -.. code-block:: nim - :test: "nim c $1" + ```nim test = "nim c $1" echo "Counting to ten: " for i in countup(1, 10): echo i + ``` -Can a `countup `_ proc be written that +Can a [countup](system.html#countup.i,T,T,Positive) proc be written that supports this loop? Let's try: -.. code-block:: nim + ```nim proc countup(a, b: int): int = var res = a while res <= b: return res inc(res) + ``` However, this does not work. The problem is that the procedure should not only `return`, but return and **continue** after an iteration has @@ -928,13 +944,13 @@ finished. This *return and continue* is called a `yield` statement. Now the only thing left to do is to replace the `proc` keyword by `iterator` and here it is -- our first iterator: -.. code-block:: nim - :test: "nim c $1" + ```nim test = "nim c $1" iterator countup(a, b: int): int = var res = a while res <= b: yield res inc(res) + ``` Iterators look very similar to procedures, but there are several important differences: @@ -948,12 +964,13 @@ important differences: (This restriction will be gone in a future version of the compiler.) However, you can also use a closure iterator to get a different set of -restrictions. See `first-class iterators `_ +restrictions. See [first-class iterators]( +manual.html#iterators-and-the-for-statement-firstminusclass-iterators) for details. Iterators can have the same name and parameters as a proc since -essentially they have their own namespaces. Therefore it is common practice to +essentially they have their own namespaces. Therefore, it is common to wrap iterators in procs of the same name which accumulate the result of the -iterator and return it as a sequence, like `split` from the `strutils module -`_. +iterator and return it as a sequence, like `split` from the [strutils module]( +strutils.html). Basic types @@ -966,18 +983,18 @@ Booleans -------- Nim's boolean type is called `bool` and consists of the two -pre-defined values `true` and `false`. Conditions in while, -if, elif, and when statements must be of type bool. +pre-defined values `true` and `false`. Conditions in `while`, +`if`, `elif`, and `when` statements must be of type bool. The operators `not, and, or, xor, <, <=, >, >=, !=, ==` are defined for the bool type. The `and` and `or` operators perform short-circuit evaluation. For example: -.. code-block:: nim - + ```nim while p != nil and p.name != "xyz": # p.name is not evaluated if p == nil p = p.next + ``` Characters @@ -985,7 +1002,7 @@ Characters The *character type* is called `char`. Its size is always one byte, so it cannot represent most UTF-8 characters, but it *can* represent one of the bytes -that makes up a multi-byte UTF-8 character. +that makes up a multibyte UTF-8 character. The reason for this is efficiency: for the overwhelming majority of use-cases, the resulting programs will still handle UTF-8 properly as UTF-8 was especially designed for this. @@ -1029,13 +1046,13 @@ The default integer type is `int`. Integer literals can have a *type suffix* to specify a non-default integer type: -.. code-block:: nim - :test: "nim c $1" + ```nim test = "nim c $1" let x = 0 # x is of type `int` y = 0'i8 # y is of type `int8` z = 0'i32 # z is of type `int32` u = 0'u # u is of type `uint` + ``` Most often integers are used for counting objects that reside in memory, so `int` has the same size as a pointer. @@ -1067,20 +1084,20 @@ The default float type is `float`. In the current implementation, Float literals can have a *type suffix* to specify a non-default float type: -.. code-block:: nim - :test: "nim c $1" + ```nim test = "nim c $1" var x = 0.0 # x is of type `float` y = 0.0'f32 # y is of type `float32` z = 0.0'f64 # z is of type `float64` + ``` The common operators `+ - * / < <= == != > >=` are defined for floats and follow the IEEE-754 standard. Automatic type conversion in expressions with different kinds of floating-point types is performed: the smaller type is converted to the larger. Integer types are **not** converted to floating-point types automatically, nor vice -versa. Use the `toInt `_ and -`toFloat `_ procs for these conversions. +versa. Use the [toInt](system.html#toInt,float) and +[toFloat](system.html#toFloat,int) procs for these conversions. Type Conversion @@ -1089,30 +1106,29 @@ Type Conversion Conversion between numerical types is performed by using the type as a function: -.. code-block:: nim - :test: "nim c $1" + ```nim test = "nim c $1" var x: int32 = 1.int32 # same as calling int32(1) y: int8 = int8('a') # 'a' == 97'i8 z: float = 2.5 # int(2.5) rounds down to 2 sum: int = int(x) + int(y) + int(z) # sum == 100 + ``` Internal type representation ============================ -As mentioned earlier, the built-in `$ `_ (stringify) operator +As mentioned earlier, the built-in [$](dollars.html) (stringify) operator turns any basic type into a string, which you can then print to the console using the `echo` proc. However, advanced types, and your own custom types, won't work with the `$` operator until you define it for them. Sometimes you just want to debug the current value of a complex type without -having to write its `$` operator. You can use then the `repr -`_ proc which works with any type and even complex data +having to write its `$` operator. You can use then the [repr]( +system.html#repr,T) proc which works with any type and even complex data graphs with cycles. The following example shows that even for basic types there is a difference between the `$` and `repr` outputs: -.. code-block:: nim - :test: "nim c $1" + ```nim test = "nim c $1" var myBool = true myCharacter = 'n' @@ -1129,6 +1145,7 @@ there is a difference between the `$` and `repr` outputs: # --> 42:42 echo myFloat, ":", repr(myFloat) # --> 3.14:3.14 + ``` Advanced types @@ -1136,11 +1153,11 @@ Advanced types In Nim new types can be defined within a `type` statement: -.. code-block:: nim - :test: "nim c $1" + ```nim test = "nim c $1" type biggestInt = int64 # biggest integer type that is available biggestFloat = float64 # biggest float type that is available + ``` Enumeration and object types may only be defined within a `type` statement. @@ -1154,15 +1171,14 @@ These values are a set of ordered symbols. Each symbol is mapped to an integer value internally. The first symbol is represented at runtime by 0, the second by 1, and so on. For example: -.. code-block:: nim - :test: "nim c $1" - + ```nim test = "nim c $1" type Direction = enum north, east, south, west var x = south # `x` is of type `Direction`; its value is `south` echo x # prints "south" + ``` All the comparison operators can be used with enumeration types. @@ -1185,9 +1201,9 @@ subranges) are called ordinal types. Ordinal types have quite a few special operations: ------------------ -------------------------------------------------------- +================= ======================================================== Operation Comment ------------------ -------------------------------------------------------- +================= ======================================================== `ord(x)` returns the integer value that is used to represent `x`'s value `inc(x)` increments `x` by one @@ -1198,11 +1214,11 @@ Operation Comment `succ(x, n)` returns the `n`'th successor of `x` `pred(x)` returns the predecessor of `x` `pred(x, n)` returns the `n`'th predecessor of `x` ------------------ -------------------------------------------------------- +================= ======================================================== -The `inc `_, `dec `_, `succ -`_ and `pred `_ operations can +The [inc](system.html#inc,T,int), [dec](system.html#dec,T,int), [succ]( +system.html#succ,T,int) and [pred](system.html#pred,T,int) operations can fail by raising an `RangeDefect` or `OverflowDefect`. (If the code has been compiled with the proper runtime checks turned on.) @@ -1213,10 +1229,10 @@ Subranges A subrange type is a range of values from an integer or enumeration type (the base type). Example: -.. code-block:: nim - :test: "nim c $1" + ```nim test = "nim c $1" type MySubrange = range[0..5] + ``` `MySubrange` is a subrange of `int` which can only hold the values 0 @@ -1224,8 +1240,8 @@ to 5. Assigning any other value to a variable of type `MySubrange` is a compile-time or runtime error. Assignments from the base type to one of its subrange types (and vice versa) are allowed. -The `system` module defines the important `Natural `_ -type as `range[0..high(int)]` (`high `_ returns +The `system` module defines the important [Natural](system.html#Natural) +type as `range[0..high(int)]` ([high](system.html#high,typedesc[T]) returns the maximal value). Other programming languages may suggest the use of unsigned integers for natural numbers. This is often **unwise**: you don't want unsigned arithmetic (which wraps around) just because the numbers cannot be negative. @@ -1245,9 +1261,7 @@ an array has the same type. The array's index type can be any ordinal type. Arrays can be constructed using `[]`: -.. code-block:: nim - :test: "nim c $1" - + ```nim test = "nim c $1" type IntArray = array[0..5, int] # an array that is indexed with 0..5 var @@ -1255,6 +1269,7 @@ Arrays can be constructed using `[]`: x = [1, 2, 3, 4, 5, 6] for i in low(x) .. high(x): echo x[i] + ``` The notation `x[i]` is used to access the i-th element of `x`. Array access is always bounds checked (at compile-time or at runtime). These @@ -1264,13 +1279,12 @@ checks can be disabled via pragmas or invoking the compiler with the Arrays are value types, like any other Nim type. The assignment operator copies the whole array contents. -The built-in `len `_ proc returns the array's -length. `low(a) `_ returns the lowest valid index -for the array `a` and `high(a) `_ the highest +The built-in [len](system.html#len,TOpenArray) proc returns the array's +length. [low(a)](system.html#low,openArray[T]) returns the lowest valid index +for the array `a` and [high(a)](system.html#high,openArray[T]) the highest valid index. -.. code-block:: nim - :test: "nim c $1" + ```nim test = "nim c $1" type Direction = enum north, east, south, west @@ -1286,6 +1300,7 @@ valid index. echo low(level) # --> north echo len(level) # --> 4 echo high(level) # --> west + ``` The syntax for nested arrays (multidimensional) in other languages is a matter of appending more brackets because usually each dimension is restricted to the @@ -1295,7 +1310,7 @@ the previous example where a level is defined as an array of enums indexed by yet another enum, we can add the following lines to add a light tower type subdivided into height levels accessed through their integer index: -.. code-block:: nim + ```nim type LightTower = array[1..10, LevelSetting] var @@ -1308,21 +1323,22 @@ subdivided into height levels accessed through their integer index: # The following lines don't compile due to type mismatch errors #tower[north][east] = on #tower[0][1] = on + ``` Note how the built-in `len` proc returns only the array's first dimension length. Another way of defining the `LightTower` to better illustrate its nested nature would be to omit the previous definition of the `LevelSetting` type and instead write it embedded directly as the type of the first dimension: -.. code-block:: nim + ```nim type LightTower = array[1..10, array[north..west, BlinkLights]] + ``` It is quite common to have arrays start at zero, so there's a shortcut syntax to specify a range from zero to the specified index minus one: -.. code-block:: nim - :test: "nim c $1" + ```nim test = "nim c $1" type IntArray = array[0..5, int] # an array that is indexed with 0..5 QuickArray = array[6, int] # an array that is indexed with 0..5 @@ -1333,6 +1349,7 @@ to specify a range from zero to the specified index minus one: y = x for i in low(x) .. high(x): echo x[i], y[i] + ``` Sequences @@ -1342,40 +1359,38 @@ Sequences are similar to arrays but of dynamic length which may change during runtime (like strings). Since sequences are resizable they are always allocated on the heap and garbage collected. -Sequences are always indexed with an `int` starting at position 0. The `len -`_, `low `_ and `high -`_ operations are available for sequences too. +Sequences are always indexed with an `int` starting at position 0. The [len]( +system.html#len,seq[T]), [low](system.html#low,openArray[T]) and [high]( +system.html#high,openArray[T]) operations are available for sequences too. The notation `x[i]` can be used to access the i-th element of `x`. Sequences can be constructed by the array constructor `[]` in conjunction with the array to sequence operator `@`. Another way to allocate space for -a sequence is to call the built-in `newSeq `_ procedure. +a sequence is to call the built-in [newSeq](system.html#newSeq) procedure. A sequence may be passed to an openarray parameter. Example: -.. code-block:: nim - :test: "nim c $1" - + ```nim test = "nim c $1" var x: seq[int] # a reference to a sequence of integers x = @[1, 2, 3, 4, 5, 6] # the @ turns the array into a sequence allocated on the heap + ``` Sequence variables are initialized with `@[]`. The `for` statement can be used with one or two variables when used with a sequence. When you use the one variable form, the variable will hold the value provided by the sequence. The `for` statement is looping over the results -from the `items() `_ iterator from the `system -`_ module. But if you use the two-variable form, the first +from the [items()](iterators.html#items.i,seq[T]) iterator from the [system]( +system.html) module. But if you use the two-variable form, the first variable will hold the index position and the second variable will hold the value. Here the `for` statement is looping over the results from the -`pairs() `_ iterator from the `system -`_ module. Examples: +[pairs()](iterators.html#pairs.i,seq[T]) iterator from the [system]( +system.html) module. Examples: -.. code-block:: nim - :test: "nim c $1" + ```nim test = "nim c $1" for value in @[3, 4, 5]: echo value # --> 3 @@ -1387,6 +1402,7 @@ value. Here the `for` statement is looping over the results from the # --> index: 0, value:3 # --> index: 1, value:4 # --> index: 2, value:5 + ``` Open arrays @@ -1397,13 +1413,12 @@ Open arrays Often fixed-size arrays turn out to be too inflexible; procedures should be able to deal with arrays of different sizes. The `openarray`:idx: type allows this. Openarrays are always indexed with an `int` starting at position 0. -The `len `_, `low `_ -and `high `_ operations are available for open +The [len](system.html#len,TOpenArray), [low](system.html#low,openArray[T]) +and [high](system.html#high,openArray[T]) operations are available for open arrays too. Any array with a compatible base type can be passed to an openarray parameter, the index type does not matter. -.. code-block:: nim - :test: "nim c $1" + ```nim test = "nim c $1" var fruits: seq[string] # reference to a sequence of strings that is initialized with '@[]' capitals: array[3, string] # array of strings with a fixed size @@ -1417,6 +1432,7 @@ openarray parameter, the index type does not matter. assert openArraySize(fruits) == 2 # procedure accepts a sequence as parameter assert openArraySize(capitals) == 3 # but also an array type + ``` The openarray type cannot be nested: multidimensional openarrays are not supported because this is seldom needed and cannot be done efficiently. @@ -1430,8 +1446,7 @@ also a means to implement passing a variable number of arguments to a procedure. The compiler converts the list of arguments to an array automatically: -.. code-block:: nim - :test: "nim c $1" + ```nim test = "nim c $1" proc myWriteln(f: File, a: varargs[string]) = for s in items(a): write(f, s) @@ -1440,13 +1455,13 @@ to an array automatically: myWriteln(stdout, "abc", "def", "xyz") # is transformed by the compiler to: myWriteln(stdout, ["abc", "def", "xyz"]) + ``` This transformation is only done if the varargs parameter is the last parameter in the procedure header. It is also possible to perform type conversions in this context: -.. code-block:: nim - :test: "nim c $1" + ```nim test = "nim c $1" proc myWriteln(f: File, a: varargs[string, `$`]) = for s in items(a): write(f, s) @@ -1455,9 +1470,10 @@ type conversions in this context: myWriteln(stdout, 123, "abc", 4.0) # is transformed by the compiler to: myWriteln(stdout, [$123, $"abc", $4.0]) + ``` -In this example `$ `_ is applied to any argument that is passed -to the parameter `a`. Note that `$ `_ applied to strings is a +In this example [$](dollars.html) is applied to any argument that is passed +to the parameter `a`. Note that [$](dollars.html) applied to strings is a nop. @@ -1469,9 +1485,7 @@ context. A slice is just an object of type Slice which contains two bounds, `a` and `b`. By itself a slice is not very useful, but other collection types define operators which accept Slice objects to define ranges. -.. code-block:: nim - :test: "nim c $1" - + ```nim test = "nim c $1" var a = "Nim is a programming language" b = "Slices are useless." @@ -1479,29 +1493,30 @@ define operators which accept Slice objects to define ranges. echo a[7 .. 12] # --> 'a prog' b[11 .. ^2] = "useful" echo b # --> 'Slices are useful.' + ``` In the previous example slices are used to modify a part of a string. The slice's bounds can hold any value supported by their type, but it is the proc using the slice object which defines what values are accepted. -To understand some of the different ways of specifying the indices of +To understand the different ways of specifying the indices of strings, arrays, sequences, etc., it must be remembered that Nim uses zero-based indices. So the string `b` is of length 19, and two different ways of specifying the indices are -.. code-block:: nim - + ```nim "Slices are useless." | | | 0 11 17 using indices ^19 ^8 ^2 using ^ syntax + ``` where `b[0 .. ^1]` is equivalent to `b[0 .. b.len-1]` and `b[0 ..< b.len]`, and it -can be seen that the `^1` provides a short-hand way of specifying the `b.len-1`. See -the `backwards index operator `_. +can be seen that the `^1` provides a shorthand way of specifying the `b.len-1`. See +the [backwards index operator](system.html#^.t%2Cint). In the above example, because the string ends in a period, to get the portion of the string that is "useless" and replace it with "useful". @@ -1512,7 +1527,7 @@ string that is "useless" and replace it with "useful". Note 1: alternate ways of writing this are `b[^8 .. ^2] = "useful"` or as `b[11 .. b.len-2] = "useful"` or as `b[11 ..< b.len-1] = "useful"`. -Note 2: As the `^` template returns a `distinct int `_ +Note 2: As the `^` template returns a [distinct int](manual.html#types-distinct-type) of type `BackwardsIndex`, we can have a `lastIndex` constant defined as `const lastIndex = ^1`, and later used as `b[0 .. lastIndex]`. @@ -1528,7 +1543,7 @@ Each object type `Foo` has a constructor `Foo(field: value, ...)` where all of its fields can be initialized. Unspecified fields will get their default value. -.. code-block:: nim + ```nim type Person = object name: string @@ -1555,18 +1570,18 @@ get their default value. # unspecified members will be initialized with their default # values. In this case it is the empty string. doAssert person4.name == "" + ``` Object fields that should be visible from outside the defining module have to be marked with `*`. -.. code-block:: nim - :test: "nim c $1" - + ```nim test = "nim c $1" type Person* = object # the type is visible from other modules name*: string # the field of this type is visible from other modules age*: int + ``` Tuples ------ @@ -1588,8 +1603,7 @@ tuple's field. Another notation that is not available for objects is `t[i]` to access the `i`'th field. Here `i` must be a constant integer. -.. code-block:: nim - :test: "nim c $1" + ```nim test = "nim c $1" type # type representing a person: # A person consists of a name and an age. @@ -1638,6 +1652,7 @@ integer. #person = building # --> Error: type mismatch: got (tuple[street: string, number: int]) # but expected 'Person' + ``` Even though you don't need to declare a type for a tuple to use it, tuples created with different field names will be considered different objects despite @@ -1645,16 +1660,14 @@ having the same field types. Tuples can be *unpacked* during variable assignment. This can be handy to assign directly the fields of the tuples to individually named -variables. An example of this is the `splitFile `_ -proc from the `os module `_ which returns the directory, name, and +variables. An example of this is the [splitFile](os.html#splitFile,string) +proc from the [os module](os.html) which returns the directory, name, and extension of a path at the same time. For tuple unpacking to work you must use parentheses around the values you want to assign the unpacking to, otherwise, you will be assigning the same value to all the individual variables! For example: -.. code-block:: nim - :test: "nim c $1" - + ```nim test = "nim c $1" import std/os let @@ -1669,11 +1682,11 @@ variables! For example: echo baddir echo badname echo badext + ``` Tuple unpacking is also supported in for-loops: -.. code-block:: nim - :test: "nim c $1" + ```nim test = "nim c $1" let a = [(10, 'a'), (20, 'b'), (30, 'c')] for (x, c) in a: @@ -1684,8 +1697,9 @@ Tuple unpacking is also supported in for-loops: for i, (x, c) in a: echo i, c # This will output: 0a; 1b; 2c + ``` -Fields of tuples are always public, they don't need to be explicity +Fields of tuples are always public, they don't need to be explicitly marked to be exported, unlike for example fields in an object type. @@ -1699,7 +1713,7 @@ point to and modify the same location in memory. Nim distinguishes between `traced`:idx: and `untraced`:idx: references. Untraced references are also called *pointers*. Traced references point to objects in a garbage-collected heap, untraced references point to -manually allocated objects or objects elsewhere in memory. Thus +manually allocated objects or objects elsewhere in memory. Thus, untraced references are *unsafe*. However, for certain low-level operations (e.g. accessing the hardware), untraced references are necessary. @@ -1711,9 +1725,7 @@ meaning to retrieve the item the reference points to. The `.` (access a tuple/object field operator) and `[]` (array/string/sequence index operator) operators perform implicit dereferencing operations for reference types: -.. code-block:: nim - :test: "nim c $1" - + ```nim test = "nim c $1" type Node = ref object le, ri: Node @@ -1722,16 +1734,17 @@ operators perform implicit dereferencing operations for reference types: var n = Node(data: 9) echo n.data # no need to write n[].data; in fact n[].data is highly discouraged! + ``` To allocate a new traced object, the built-in procedure `new` can be used: -.. code-block:: nim - - var n: Node - new(n) + ```nim + var n: Node + new(n) + ``` To deal with untraced memory, the procedures `alloc`, `dealloc` and -`realloc` can be used. The `system `_ +`realloc` can be used. The [system](system.html) module's documentation contains further details. If a reference points to *nothing*, it has the value `nil`. @@ -1747,8 +1760,7 @@ techniques. Example: -.. code-block:: nim - :test: "nim c $1" + ```nim test = "nim c $1" proc greet(name: string): string = "Hello, " & name & "!" @@ -1760,11 +1772,12 @@ Example: communicate(greet, "John") communicate(bye, "Mary") + ``` A subtle issue with procedural types is that the calling convention of the procedure influences the type compatibility: procedural types are only compatible if they have the same calling convention. The different calling conventions are -listed in the `manual `_. +listed in the [manual](manual.html#types-procedural-type). Distinct type ------------- @@ -1774,7 +1787,7 @@ subtype relationship between it and its base type". You must **explicitly** define all behavior for the distinct type. To help with this, both the distinct type and its base type can cast from one type to the other. -Examples are provided in the `manual `_. +Examples are provided in the [manual](manual.html#types-distinct-type). Modules ======= @@ -1785,7 +1798,7 @@ Each module is in its own file. Modules enable `information hiding`:idx: and module by using the `import`:idx: statement. Only top-level symbols that are marked with an asterisk (`*`) are exported: -.. code-block:: nim + ```nim # Module A var x*, y: int @@ -1799,6 +1812,7 @@ with an asterisk (`*`) are exported: when isMainModule: # test the new `*` operator for sequences: assert(@[1, 2, 3] * @[1, 2, 3] == @[1, 4, 9]) + ``` The above module exports `x` and `*`, but not `y`. @@ -1814,15 +1828,17 @@ a symbol is ambiguous, it *must* be qualified. A symbol is ambiguous if it is defined in two (or more) different modules and both modules are imported by a third one: -.. code-block:: nim + ```nim # Module A var x*: string + ``` -.. code-block:: nim + ```nim # Module B var x*: int + ``` -.. code-block:: nim + ```nim # Module C import A, B write(stdout, x) # error: x is ambiguous @@ -1830,20 +1846,23 @@ imported by a third one: var x = 4 write(stdout, x) # not ambiguous: uses the module C's x + ``` But this rule does not apply to procedures or iterators. Here the overloading rules apply: -.. code-block:: nim + ```nim # Module A proc x*(a: int): string = $a + ``` -.. code-block:: nim + ```nim # Module B proc x*(a: string): string = $a + ``` -.. code-block:: nim + ```nim # Module C import A, B write(stdout, x(3)) # no error: A.x is called @@ -1851,6 +1870,7 @@ rules apply: proc x*(a: int): string = discard write(stdout, x(3)) # ambiguous: which `x` is to call? + ``` Excluding symbols @@ -1860,8 +1880,9 @@ The normal `import` statement will bring in all exported symbols. These can be limited by naming symbols that should be excluded using the `except` qualifier. -.. code-block:: nim + ```nim import mymodule except y + ``` From statement @@ -1871,32 +1892,36 @@ We have already seen the simple `import` statement that just imports all exported symbols. An alternative that only imports listed symbols is the `from import` statement: -.. code-block:: nim + ```nim from mymodule import x, y, z + ``` The `from` statement can also force namespace qualification on symbols, thereby making symbols available, but needing to be qualified in order to be used. -.. code-block:: nim + ```nim from mymodule import x, y, z x() # use x without any qualification + ``` -.. code-block:: nim + ```nim from mymodule import nil mymodule.x() # must qualify x with the module name as prefix x() # using x here without qualification is a compile error + ``` Since module names are generally long to be descriptive, you can also define a shorter alias to use when qualifying symbols. -.. code-block:: nim + ```nim from mymodule as m import nil m.x() # m is aliasing mymodule + ``` Include statement @@ -1906,8 +1931,9 @@ The `include` statement does something fundamentally different than importing a module: it merely includes the contents of a file. The `include` statement is useful to split up a large module into several files: -.. code-block:: nim + ```nim include fileA, fileB, fileC + ``` @@ -1915,7 +1941,7 @@ Part 2 ====== So, now that we are done with the basics, let's see what Nim offers apart -from a nice syntax for procedural programming: `Part II `_ +from a nice syntax for procedural programming: [Part II](tut2.html) .. _strutils: strutils.html diff --git a/doc/tut2.rst b/doc/tut2.md similarity index 92% rename from doc/tut2.rst rename to doc/tut2.md index 725f68dd55dd..3c858c64e1cc 100644 --- a/doc/tut2.rst +++ b/doc/tut2.md @@ -13,11 +13,11 @@ Nim Tutorial (Part II) Introduction ============ - "Repetition renders the ridiculous reasonable." -- Norman Wildberger +> "Repetition renders the ridiculous reasonable." -- Norman Wildberger This document is a tutorial for the advanced constructs of the *Nim* programming language. **Note that this document is somewhat obsolete as the** -`manual `_ **contains many more examples of the advanced language +[manual](manual.html) **contains many more examples of the advanced language features.** @@ -27,8 +27,8 @@ Pragmas Pragmas are Nim's method to give the compiler additional information/ commands without introducing a massive number of new keywords. Pragmas are enclosed in the special `{.` and `.}` curly dot brackets. This tutorial -does not cover pragmas. See the `manual `_ or `user guide -`_ for a description of the available +does not cover pragmas. See the [manual](manual.html#pragmas) or [user guide]( +nimc.html#additional-features) for a description of the available pragmas. @@ -53,8 +53,7 @@ types with inheritance are also marked as `ref` types even though this isn't strictly enforced. To check at runtime if an object is of a certain type, the `of` operator can be used. -.. code-block:: nim - :test: "nim c $1" + ```nim test = "nim c $1" type Person = ref object of RootObj name*: string # the * means that `name` is accessible from other modules @@ -70,6 +69,7 @@ type, the `of` operator can be used. # object construction: student = Student(name: "Anton", age: 5, id: 2) echo student[] + ``` Inheritance is done with the `object of` syntax. Multiple inheritance is currently not supported. If an object type has no suitable ancestor, `RootObj` @@ -97,8 +97,7 @@ would require arbitrary symbol lookahead which slows down compilation.) Example: -.. code-block:: nim - :test: "nim c $1" + ```nim test = "nim c $1" type Node = ref object # a reference to an object with the following field: le, ri: Node # left and right subtrees @@ -108,6 +107,7 @@ Example: name: string # the symbol's name line: int # the line the symbol was declared in code: Node # the symbol's abstract syntax tree + ``` Type conversions @@ -124,9 +124,10 @@ raised. The syntax for type conversions is `destination_type(expression_to_convert)` (like an ordinary call): -.. code-block:: nim + ```nim proc getID(x: Person): int = Student(x).id + ``` The `InvalidObjectConversionDefect` exception is raised if `x` is not a `Student`. @@ -139,9 +140,7 @@ variant types are needed. An example: -.. code-block:: nim - :test: "nim c $1" - + ```nim test = "nim c $1" # This is an example how an abstract syntax tree could be modelled in Nim type NodeKind = enum # the different node types @@ -165,6 +164,7 @@ An example: # the following statement raises an `FieldDefect` exception, because # n.kind's value does not fit: n.strVal = "" + ``` As can been seen from the example, an advantage to an object hierarchy is that no conversion between different object types is needed. Yet, access to invalid @@ -183,27 +183,27 @@ If there are no remaining arguments, the parentheses can be omitted: This method call syntax is not restricted to objects, it can be used for any type: -.. code-block:: nim - :test: "nim c $1" + ```nim test = "nim c $1" import std/strutils echo "abc".len # is the same as echo len("abc") echo "abc".toUpperAscii() echo({'a', 'b', 'c'}.card) stdout.writeLine("Hallo") # the same as writeLine(stdout, "Hallo") + ``` (Another way to look at the method call syntax is that it provides the missing postfix notation.) So "pure object oriented" code is easy to write: -.. code-block:: nim - :test: "nim c $1" + ```nim test = "nim c $1" import std/[strutils, sequtils] stdout.writeLine("Give a list of numbers (separated by spaces): ") stdout.write(stdin.readLine.splitWhitespace.map(parseInt).max.`$`) stdout.writeLine(" is the maximum!") + ``` Properties @@ -213,9 +213,7 @@ Ordinary get-procedures that are called with the *method call syntax* achieve the same. But setting a value is different; for this a special setter syntax is needed: -.. code-block:: nim - :test: "nim c $1" - + ```nim test = "nim c $1" type Socket* = ref object of RootObj h: int # cannot be accessed from the outside of the module due to missing star @@ -231,6 +229,7 @@ is needed: var s: Socket new s s.host = 34 # same as `host=`(s, 34) + ``` (The example also shows `inline` procedures.) @@ -238,8 +237,7 @@ is needed: The `[]` array access operator can be overloaded to provide `array properties`:idx:\ : -.. code-block:: nim - :test: "nim c $1" + ```nim test = "nim c $1" type Vector* = object x, y, z: float @@ -259,6 +257,7 @@ The `[]` array access operator can be overloaded to provide of 1: result = v.y of 2: result = v.z else: assert(false) + ``` The example is silly, since a vector is better modelled by a tuple which already provides `v[]` access. @@ -270,8 +269,7 @@ Dynamic dispatch Procedures always use static dispatch. For dynamic dispatch replace the `proc` keyword by `method`: -.. code-block:: nim - :test: "nim c $1" + ```nim test = "nim c $1" type Expression = ref object of RootObj ## abstract base class for an expression Literal = ref object of Expression @@ -291,6 +289,7 @@ Procedures always use static dispatch. For dynamic dispatch replace the proc newPlus(a, b: Expression): PlusExpr = PlusExpr(a: a, b: b) echo eval(newPlus(newPlus(newLit(1), newLit(2)), newLit(4))) + ``` Note that in the example the constructors `newLit` and `newPlus` are procs because it makes more sense for them to use static binding, but `eval` is a @@ -302,9 +301,7 @@ method because it requires dynamic binding. In a multi-method all parameters that have an object type are used for the dispatching: -.. code-block:: nim - :test: "nim c --multiMethods:on $1" - + ```nim test = "nim c --multiMethods:on $1" type Thing = ref object of RootObj Unit = ref object of Thing @@ -323,6 +320,7 @@ dispatching: new a new b collide(a, b) # output: 2 + ``` As the example demonstrates, invocation of a multi-method cannot be ambiguous: @@ -339,7 +337,7 @@ Exceptions ========== In Nim exceptions are objects. By convention, exception types are -suffixed with 'Error'. The `system `_ module defines an +suffixed with 'Error'. The [system](system.html) module defines an exception hierarchy that you might want to stick to. Exceptions derive from `system.Exception`, which provides the common interface. @@ -355,20 +353,21 @@ Raise statement --------------- Raising an exception is done with the `raise` statement: -.. code-block:: nim - :test: "nim c $1" + ```nim test = "nim c $1" var e: ref OSError new(e) e.msg = "the request to the OS failed" raise e + ``` If the `raise` keyword is not followed by an expression, the last exception is *re-raised*. For the purpose of avoiding repeating this common code pattern, the template `newException` in the `system` module can be used: -.. code-block:: nim + ```nim raise newException(OSError, "the request to the OS failed") + ``` Try statement @@ -376,8 +375,7 @@ Try statement The `try` statement handles exceptions: -.. code-block:: nim - :test: "nim c $1" + ```nim test = "nim c $1" from std/strutils import parseInt # read the first two lines of a text file that should contain numbers @@ -401,6 +399,7 @@ The `try` statement handles exceptions: raise finally: close(f) + ``` The statements after the `try` are executed unless an exception is raised. Then the appropriate `except` part is executed. @@ -418,12 +417,12 @@ the rest of the procedure - that is not within a `finally` clause - is not executed (if an exception occurs). If you need to *access* the actual exception object or message inside an -`except` branch you can use the `getCurrentException() -`_ and `getCurrentExceptionMsg() -`_ procs from the `system `_ +`except` branch you can use the [getCurrentException()]( +system.html#getCurrentException) and [getCurrentExceptionMsg()]( +system.html#getCurrentExceptionMsg) procs from the [system](system.html) module. Example: -.. code-block:: nim + ```nim try: doSomethingHere() except: @@ -431,6 +430,7 @@ module. Example: e = getCurrentException() msg = getCurrentExceptionMsg() echo "Got exception ", repr(e), " with message ", msg + ``` Annotating procs with raised exceptions @@ -443,12 +443,13 @@ instance, if you specify that a proc raises `IOError`, and at some point it (or one of the procs it calls) starts raising a new exception the compiler will prevent that proc from compiling. Usage example: -.. code-block:: nim + ```nim proc complexProc() {.raises: [IOError, ArithmeticDefect].} = ... proc simpleProc() {.raises: [].} = ... + ``` Once you have code like this in place, if the list of raised exception changes the compiler will stop with an error specifying the line of the proc which @@ -463,7 +464,7 @@ tracking is part of Nim's effect system). Another more roundabout way to find out the list of exceptions raised by a proc is to use the Nim ``doc`` command which generates documentation for a whole module and decorates all procs with the list of raised exceptions. You can read more about Nim's -`effect system and related pragmas in the manual `_. +[effect system and related pragmas in the manual](manual.html#effect-system). Generics @@ -474,8 +475,7 @@ with `type parameters`:idx:. Generic parameters are written within square brackets, for example `Foo[T]`. They are most useful for efficient type safe containers: -.. code-block:: nim - :test: "nim c $1" + ```nim test = "nim c $1" type BinaryTree*[T] = ref object # BinaryTree is a generic type with # generic param `T` @@ -530,6 +530,7 @@ containers: add(root, "world") # instantiates the second `add` proc for str in preorder(root): stdout.writeLine(str) + ``` The example shows a generic binary tree. Depending on context, the brackets are used either to introduce type parameters or to instantiate a generic proc, @@ -539,8 +540,7 @@ is not hidden and is used in the `preorder` iterator. There is a special `[:T]` syntax when using generics with the method call syntax: -.. code-block:: nim - :test: "nim c $1" + ```nim test = "nim c $1" proc foo[T](i: T) = discard @@ -549,6 +549,7 @@ There is a special `[:T]` syntax when using generics with the method call syntax # i.foo[int]() # Error: expression 'foo(i)' has no type (or is ambiguous) i.foo[:int]() # Success + ``` Templates @@ -563,12 +564,13 @@ To *invoke* a template, call it like a procedure. Example: -.. code-block:: nim + ```nim template `!=` (a, b: untyped): untyped = # this definition exists in the System module not (a == b) assert(5 != 6) # the compiler rewrites that to: assert(not (5 == 6)) + ``` The `!=`, `>`, `>=`, `in`, `notin`, `isnot` operators are in fact templates: this has the benefit that if you overload the `==` operator, @@ -582,8 +584,7 @@ for IEEE floating point numbers - NaN breaks basic boolean logic.) Templates are especially useful for lazy evaluation purposes. Consider a simple proc for logging: -.. code-block:: nim - :test: "nim c $1" + ```nim test = "nim c $1" const debug = true @@ -593,6 +594,7 @@ simple proc for logging: var x = 4 log("x has the value: " & $x) + ``` This code has a shortcoming: if `debug` is set to false someday, the quite expensive `$` and `&` operations are still performed! (The argument @@ -600,8 +602,7 @@ evaluation for procedures is *eager*). Turning the `log` proc into a template solves this problem: -.. code-block:: nim - :test: "nim c $1" + ```nim test = "nim c $1" const debug = true @@ -611,6 +612,7 @@ Turning the `log` proc into a template solves this problem: var x = 4 log("x has the value: " & $x) + ``` The parameters' types can be ordinary types or the meta types `untyped`, `typed`, or `type`. `type` suggests that only a type symbol may be given @@ -622,9 +624,7 @@ If the template has no explicit return type, To pass a block of statements to a template, use `untyped` for the last parameter: -.. code-block:: nim - :test: "nim c $1" - + ```nim test = "nim c $1" template withFile(f: untyped, filename: string, mode: FileMode, body: untyped) = let fn = filename @@ -640,6 +640,7 @@ To pass a block of statements to a template, use `untyped` for the last paramete withFile(txt, "ttempl3.txt", fmWrite): txt.writeLine("line 1") txt.writeLine("line 2") + ``` In the example the two `writeLine` statements are bound to the `body` parameter. The `withFile` template contains boilerplate code and helps to @@ -650,8 +651,7 @@ once. Example: Lifting Procs ---------------------- -.. code-block:: nim - :test: "nim c $1" + `````nim test = "nim c $1" import std/math template liftScalarProc(fname) = @@ -660,9 +660,10 @@ Example: Lifting Procs ## to provide templated procs that can handle a single ## parameter of seq[T] or nested seq[seq[]] or the same type ## - ## .. code-block:: Nim - ## liftScalarProc(abs) - ## # now abs(@[@[1,-2], @[-2,-3]]) == @[@[1,2], @[2,3]] + ## ```Nim + ## liftScalarProc(abs) + ## # now abs(@[@[1,-2], @[-2,-3]]) == @[@[1,2], @[2,3]] + ## ``` proc fname[T](x: openarray[T]): auto = var temp: T type outType = typeof(fname(temp)) @@ -672,6 +673,7 @@ Example: Lifting Procs liftScalarProc(sqrt) # make sqrt() work for sequences echo sqrt(@[4.0, 16.0, 25.0, 36.0]) # => @[2.0, 4.0, 5.0, 6.0] + ````` Compilation to JavaScript ========================= @@ -692,4 +694,4 @@ JavaScript-compatible code you should remember the following: Part 3 ====== -The next part is entirely about metaprogramming via macros: `Part III `_ +The next part is entirely about metaprogramming via macros: [Part III](tut3.html). diff --git a/doc/tut3.rst b/doc/tut3.md similarity index 90% rename from doc/tut3.rst rename to doc/tut3.md index c2c95610786b..958d3579145d 100644 --- a/doc/tut3.rst +++ b/doc/tut3.md @@ -13,7 +13,7 @@ Nim Tutorial (Part III) Introduction ============ - "With Great Power Comes Great Responsibility." -- Spider Man's Uncle +> "With Great Power Comes Great Responsibility." -- Spider Man's Uncle This document is a tutorial about Nim's macro system. A macro is a function that is executed at compile-time and transforms @@ -91,14 +91,14 @@ in the expression `foo(x)`, `x` needs to be an integer constant, but in the macro body `arg` is just like a normal parameter of type `int`. -.. code-block:: nim - + ```nim import std/macros macro myMacro(arg: static[int]): untyped = echo arg # just an int (7), not `NimNode` myMacro(1 + 2 * 3) + ``` Code Blocks as Arguments @@ -108,12 +108,12 @@ It is possible to pass the last argument of a call expression in a separate code block with indentation. For example, the following code example is a valid (but not a recommended) way to call `echo`: -.. code-block:: nim - + ```nim echo "Hello ": let a = "Wor" let b = "ld!" a & b + ``` For macros this way of calling is very useful; syntax trees of arbitrary complexity can be passed to macros with this notation. @@ -125,7 +125,7 @@ The Syntax Tree In order to build a Nim syntax tree one needs to know how Nim source code is represented as a syntax tree, and how such a tree needs to look like so that the Nim compiler will understand it. The nodes of the -Nim syntax tree are documented in the `macros `_ module. +Nim syntax tree are documented in the [macros](macros.html) module. But a more interactive way to explore the Nim syntax tree is with `macros.treeRepr`, it converts a syntax tree into a multi-line string for printing on the console. It can be used @@ -134,8 +134,7 @@ and for debug printing of generated syntax tree. `dumpTree` is a predefined macro that just prints its argument in a tree representation, but does nothing else. Here is an example of such a tree representation: -.. code-block:: nim - + ```nim dumpTree: var mt: MyType = MyType(a:123.456, b:"abcdef") @@ -153,6 +152,7 @@ but does nothing else. Here is an example of such a tree representation: # ExprColonExpr # Ident "b" # StrLit "abcdef" + ``` Custom Semantic Checking @@ -166,10 +166,10 @@ macro evaluation should be caught and create a nice error message. the checks need to be more complex, arbitrary error messages can be created with the `macros.error` proc. -.. code-block:: nim - + ```nim macro myAssert(arg: untyped): untyped = arg.expectKind nnkInfix + ``` Generating Code @@ -187,36 +187,36 @@ tree with calls to `newTree` and `newLit` the macro Backticks are used to insert code from `NimNode` symbols into the generated expression. -.. code-block:: nim - :test: "nim c $1" - import std/macros - macro a(i) = quote do: - let `i` = 0 + ```nim test = "nim c $1" + import std/macros + macro a(i) = quote do: + let `i` = 0 - a b - doAssert b == 0 + a b + doAssert b == 0 + ``` A custom prefix operator can be defined whenever backticks are needed. -.. code-block:: nim - :test: "nim c $1" - import std/macros - macro a(i) = quote("@") do: - assert @i == 0 + ```nim test = "nim c $1" + import std/macros + macro a(i) = quote("@") do: + assert @i == 0 - let b = 0 - a b + let b = 0 + a b + ``` The injected symbol needs accent quoted when it resolves to a symbol. -.. code-block:: nim - :test: "nim c $1" - import std/macros - macro a(i) = quote("@") do: - let `@i` = 0 + ```nim test = "nim c $1" + import std/macros + macro a(i) = quote("@") do: + let `@i` = 0 - a b - doAssert b == 0 + a b + doAssert b == 0 + ``` Make sure to inject only symbols of type `NimNode` into the generated syntax tree. You can use `newLit` to convert arbitrary values into @@ -224,9 +224,7 @@ expressions trees of type `NimNode` so that it is safe to inject them into the tree. -.. code-block:: nim - :test: "nim c $1" - + ```nim test = "nim c $1" import std/macros type @@ -246,12 +244,14 @@ them into the tree. echo `mtLit` myMacro("Hallo") + ``` The call to `myMacro` will generate the following code: -.. code-block:: nim + ```nim echo "Hallo" echo MyType(a: 123.456'f64, b: "abcdef") + ``` Building Your First Macro @@ -263,9 +263,7 @@ do is to build a simple example of the macro usage, and then just print the argument. This way it is possible to get an idea of what a correct argument should look like. -.. code-block:: nim - :test: "nim c $1" - + ```nim test = "nim c $1" import std/macros macro myAssert(arg: untyped): untyped = @@ -275,13 +273,14 @@ correct argument should look like. let b = 2 myAssert(a != b) + ``` -.. code-block:: - + ``` Infix Ident "!=" Ident "a" Ident "b" + ``` From the output, it is possible to see that the argument is an infix @@ -289,9 +288,7 @@ operator (node kind is "Infix"), as well as that the two operands are at index 1 and 2. With this information, the actual macro can be written. -.. code-block:: nim - :test: "nim c $1" - + ```nim test = "nim c $1" import std/macros macro myAssert(arg: untyped): untyped = @@ -312,6 +309,7 @@ written. myAssert(a != b) myAssert(a == b) + ``` This is the code that will be generated. To debug what the macro @@ -319,9 +317,10 @@ actually generated, the statement `echo result.repr` can be used, in the last line of the macro. It is also the statement that has been used to get this output. -.. code-block:: nim + ```nim if not (a != b): raise newException(AssertionDefect, $a & " != " & $b) + ``` With Power Comes Responsibility ------------------------------- @@ -367,7 +366,7 @@ recommended way. But still `strformat` is a good example for a practical use case for a macro that is slightly more complex than the `assert` macro. -`Strformat `_ +[Strformat](https://github.com/nim-lang/Nim/blob/5845716df8c96157a047c2bd6bcdd795a7a2b9b1/lib/pure/strformat.nim#L280) Ast Pattern Matching -------------------- @@ -376,7 +375,7 @@ Ast Pattern Matching is a macro library to aid in writing complex macros. This can be seen as a good example of how to repurpose the Nim syntax tree with new semantics. -`Ast Pattern Matching `_ +[Ast Pattern Matching](https://github.com/krux02/ast-pattern-matching) OpenGL Sandbox -------------- @@ -385,4 +384,4 @@ This project has a working Nim to GLSL compiler written entirely in macros. It scans recursively through all used function symbols to compile them so that cross library functions can be executed on the GPU. -`OpenGL Sandbox `_ +[OpenGL Sandbox](https://github.com/krux02/opengl-sandbox) diff --git a/koch.nim b/koch.nim index a3cfd593011b..e3e2cb098b1a 100644 --- a/koch.nim +++ b/koch.nim @@ -150,7 +150,7 @@ proc bundleNimbleExe(latest: bool, args: string) = commit = commit, allowBundled = true) # installer.ini expects it under $nim/bin nimCompile("dist/nimble/src/nimble.nim", - options = "-d:release --useVersion:1.6 --noNimblePath " & args) + options = "-d:release --mm:refc --useVersion:1.6 --noNimblePath " & args) proc bundleNimsuggest(args: string) = nimCompileFold("Compile nimsuggest", "nimsuggest/nimsuggest.nim", @@ -251,20 +251,6 @@ proc install(args: string) = geninstall() exec("sh ./install.sh $#" % args) -when false: - proc web(args: string) = - nimexec("js tools/dochack/dochack.nim") - nimexec("cc -r tools/nimweb.nim $# web/website.ini --putenv:nimversion=$#" % - [args, VersionAsString]) - - proc website(args: string) = - nimexec("cc -r tools/nimweb.nim $# --website web/website.ini --putenv:nimversion=$#" % - [args, VersionAsString]) - - proc pdf(args="") = - exec("$# cc -r tools/nimweb.nim $# --pdf web/website.ini --putenv:nimversion=$#" % - [findNim().quoteShell(), args, VersionAsString], additionalPATH=findNim().splitFile.dir) - # -------------- boot --------------------------------------------------------- proc findStartNim: string = @@ -332,9 +318,9 @@ proc boot(args: string) = # in order to use less memory, we split the build into two steps: # --compileOnly produces a $project.json file and does not run GCC/Clang. # jsonbuild then uses the $project.json file to build the Nim binary. - exec "$# $# $# --nimcache:$# $# --compileOnly compiler" / "nim.nim" % + exec "$# $# $# --nimcache:$# $# --noNimblePath --compileOnly compiler" / "nim.nim" % [nimi, bootOptions, extraOption, smartNimcache, args] - exec "$# jsonscript --nimcache:$# $# compiler" / "nim.nim" % + exec "$# jsonscript --noNimblePath --nimcache:$# $# compiler" / "nim.nim" % [nimi, smartNimcache, args] if sameFileContent(output, i.thVersion): diff --git a/lib/core/macros.nim b/lib/core/macros.nim index ba19712cfb46..cc2d5041e727 100644 --- a/lib/core/macros.nim +++ b/lib/core/macros.nim @@ -11,7 +11,7 @@ include "system/inclrtl" import std/private/since when defined(nimPreviewSlimSystem): - import std/assertions + import std/[assertions, formatfloat] ## This module contains the interface to the compiler's abstract syntax @@ -182,7 +182,7 @@ template `^^`(n: NimNode, i: untyped): untyped = proc `[]`*[T, U: Ordinal](n: NimNode, x: HSlice[T, U]): seq[NimNode] = ## Slice operation for NimNode. - ## Returns a seq of child of `n` who inclusive range [n[x.a], n[x.b]]. + ## Returns a seq of child of `n` who inclusive range `[n[x.a], n[x.b]]`. let xa = n ^^ x.a let L = (n ^^ x.b) - xa + 1 result = newSeq[NimNode](L) @@ -201,10 +201,9 @@ template `or`*(x, y: NimNode): NimNode = ## Evaluate `x` and when it is not an empty node, return ## it. Otherwise evaluate to `y`. Can be used to chain several ## expressions to get the first expression that is not empty. - ## - ## .. code-block:: nim - ## + ## ``` ## let node = mightBeEmpty() or mightAlsoBeEmpty() or fallbackNode + ## ``` let arg = x if arg != nil and arg.kind != nnkEmpty: @@ -395,8 +394,31 @@ proc newNimNode*(kind: NimNodeKind, ## produced code crashes. You should ensure that it is set to a node that ## you are transforming. -proc copyNimNode*(n: NimNode): NimNode {.magic: "NCopyNimNode", noSideEffect.} -proc copyNimTree*(n: NimNode): NimNode {.magic: "NCopyNimTree", noSideEffect.} +proc copyNimNode*(n: NimNode): NimNode {.magic: "NCopyNimNode", noSideEffect.} = + ## Creates a new AST node by copying the node `n`. Note that unlike `copyNimTree`, + ## child nodes of `n` are not copied. + runnableExamples: + macro foo(x: typed) = + var s = copyNimNode(x) + doAssert s.len == 0 + doAssert s.kind == nnkStmtList + + foo: + let x = 12 + echo x + +proc copyNimTree*(n: NimNode): NimNode {.magic: "NCopyNimTree", noSideEffect.} = + ## Creates a new AST node by recursively copying the node `n`. Note that + ## unlike `copyNimNode`, this copies `n`, the children of `n`, etc. + runnableExamples: + macro foo(x: typed) = + var s = copyNimTree(x) + doAssert s.len == 2 + doAssert s.kind == nnkStmtList + + foo: + let x = 12 + echo x proc error*(msg: string, n: NimNode = nil) {.magic: "NError", benign.} ## Writes an error message at compile time. The optional `n: NimNode` @@ -545,11 +567,10 @@ proc getAst*(macroOrTemplate: untyped): NimNode {.magic: "ExpandToAst", noSideEf ## Obtains the AST nodes returned from a macro or template invocation. ## See also `genasts.genAst`. ## Example: - ## - ## .. code-block:: nim - ## + ## ``` ## macro FooMacro() = ## var ast = getAst(BarTemplate()) + ## ``` proc quote*(bl: typed, op = "``"): NimNode {.magic: "QuoteAst", noSideEffect.} = ## Quasi-quoting operator. @@ -1003,18 +1024,18 @@ macro dumpTree*(s: untyped): untyped = echo s.treeRepr ## a certain expression/statement. ## ## For example: - ## - ## .. code-block:: nim - ## dumpTree: - ## echo "Hello, World!" + ## ``` + ## dumpTree: + ## echo "Hello, World!" + ## ``` ## ## Outputs: - ## - ## .. code-block:: - ## StmtList - ## Command - ## Ident "echo" - ## StrLit "Hello, World!" + ## ``` + ## StmtList + ## Command + ## Ident "echo" + ## StrLit "Hello, World!" + ## ``` ## ## Also see `dumpAstGen` and `dumpLisp`. @@ -1027,18 +1048,18 @@ macro dumpLisp*(s: untyped): untyped = echo s.lispRepr(indented = true) ## a certain expression/statement. ## ## For example: - ## - ## .. code-block:: nim - ## dumpLisp: - ## echo "Hello, World!" + ## ``` + ## dumpLisp: + ## echo "Hello, World!" + ## ``` ## ## Outputs: - ## - ## .. code-block:: - ## (StmtList - ## (Command - ## (Ident "echo") - ## (StrLit "Hello, World!"))) + ## ``` + ## (StmtList + ## (Command + ## (Ident "echo") + ## (StrLit "Hello, World!"))) + ## ``` ## ## Also see `dumpAstGen` and `dumpTree`. @@ -1050,20 +1071,20 @@ macro dumpAstGen*(s: untyped): untyped = echo s.astGenRepr ## outputs and then copying the snippets into the macro for modification. ## ## For example: - ## - ## .. code-block:: nim - ## dumpAstGen: - ## echo "Hello, World!" + ## ``` + ## dumpAstGen: + ## echo "Hello, World!" + ## ``` ## ## Outputs: - ## - ## .. code-block:: nim - ## nnkStmtList.newTree( - ## nnkCommand.newTree( - ## newIdentNode("echo"), - ## newLit("Hello, World!") - ## ) - ## ) + ## ``` + ## nnkStmtList.newTree( + ## nnkCommand.newTree( + ## newIdentNode("echo"), + ## newLit("Hello, World!") + ## ) + ## ) + ## ``` ## ## Also see `dumpTree` and `dumpLisp`. @@ -1075,7 +1096,12 @@ proc newStmtList*(stmts: varargs[NimNode]): NimNode = ## Create a new statement list. result = newNimNode(nnkStmtList).add(stmts) -proc newPar*(exprs: varargs[NimNode]): NimNode = +proc newPar*(exprs: NimNode): NimNode = + ## Create a new parentheses-enclosed expression. + newNimNode(nnkPar).add(exprs) + +proc newPar*(exprs: varargs[NimNode]): NimNode {.deprecated: + "don't use newPar/nnkPar to construct tuple expressions; use nnkTupleConstr instead".} = ## Create a new parentheses-enclosed expression. newNimNode(nnkPar).add(exprs) @@ -1128,23 +1154,22 @@ proc newIdentDefs*(name, kind: NimNode; ## `let` or `var` blocks may have an empty `kind` node if the ## identifier is being assigned a value. Example: ## - ## .. code-block:: nim - ## + ## ``` ## var varSection = newNimNode(nnkVarSection).add( ## newIdentDefs(ident("a"), ident("string")), ## newIdentDefs(ident("b"), newEmptyNode(), newLit(3))) ## # --> var ## # a: string ## # b = 3 + ## ``` ## ## If you need to create multiple identifiers you need to use the lower level ## `newNimNode`: - ## - ## .. code-block:: nim - ## + ## ``` ## result = newNimNode(nnkIdentDefs).add( ## ident("a"), ident("b"), ident("c"), ident("string"), ## newStrLitNode("Hello")) + ## ``` newNimNode(nnkIdentDefs).add(name, kind, default) proc newNilLit*(): NimNode = @@ -1191,14 +1216,12 @@ proc newProc*(name = newEmptyNode(); proc newIfStmt*(branches: varargs[tuple[cond, body: NimNode]]): NimNode = ## Constructor for `if` statements. - ## - ## .. code-block:: nim - ## - ## newIfStmt( - ## (Ident, StmtList), - ## ... - ## ) - ## + ## ``` + ## newIfStmt( + ## (Ident, StmtList), + ## ... + ## ) + ## ``` result = newNimNode(nnkIfStmt) if len(branches) < 1: error("If statement must have at least one branch") @@ -1209,17 +1232,15 @@ proc newEnum*(name: NimNode, fields: openArray[NimNode], public, pure: bool): NimNode = ## Creates a new enum. `name` must be an ident. Fields are allowed to be - ## either idents or EnumFieldDef - ## - ## .. code-block:: nim - ## - ## newEnum( - ## name = ident("Colors"), - ## fields = [ident("Blue"), ident("Red")], - ## public = true, pure = false) - ## - ## # type Colors* = Blue Red + ## either idents or EnumFieldDef: + ## ``` + ## newEnum( + ## name = ident("Colors"), + ## fields = [ident("Blue"), ident("Red")], + ## public = true, pure = false) ## + ## # type Colors* = Blue Red + ## ``` expectKind name, nnkIdent if len(fields) < 1: @@ -1360,7 +1381,9 @@ proc `$`*(node: NimNode): string = of nnkOpenSymChoice, nnkClosedSymChoice: result = $node[0] of nnkAccQuoted: - result = $node[0] + result = "" + for i in 0 ..< node.len: + result.add(repr(node[i])) else: badNodeKind node, "$" @@ -1381,10 +1404,10 @@ iterator children*(n: NimNode): NimNode {.inline.} = template findChild*(n: NimNode; cond: untyped): NimNode {.dirty.} = ## Find the first child node matching condition (or nil). - ## - ## .. code-block:: nim + ## ``` ## var res = findChild(n, it.kind == nnkPostfix and ## it.basename.ident == ident"foo") + ## ``` block: var res: NimNode for it in n.children: @@ -1489,7 +1512,7 @@ macro expandMacros*(body: typed): untyped = ## ## For instance, ## - ## .. code-block:: nim + ## ``` ## import std/[sugar, macros] ## ## let @@ -1497,6 +1520,7 @@ macro expandMacros*(body: typed): untyped = ## y = 20 ## expandMacros: ## dump(x + y) + ## ``` ## ## will actually dump `x + y`, but at the same time will print at ## compile time the expansion of the `dump` macro, which in this @@ -1521,7 +1545,7 @@ proc extractTypeImpl(n: NimNode): NimNode = else: error("Invalid node to retrieve type implementation of: " & $n.kind) proc customPragmaNode(n: NimNode): NimNode = - expectKind(n, {nnkSym, nnkDotExpr, nnkBracketExpr, nnkTypeOfExpr, nnkCheckedFieldExpr}) + expectKind(n, {nnkSym, nnkDotExpr, nnkBracketExpr, nnkTypeOfExpr, nnkType, nnkCheckedFieldExpr}) let typ = n.getTypeInst() @@ -1532,7 +1556,9 @@ proc customPragmaNode(n: NimNode): NimNode = if kind(typ[1]) == nnkBracketExpr: typ[1][0] else: typ[1] ) - if impl[0].kind == nnkPragmaExpr: + if impl.kind == nnkNilLit: + return impl + elif impl[0].kind == nnkPragmaExpr: return impl[0][1] else: return impl[0] # handle types which don't have macro at all @@ -1541,7 +1567,7 @@ proc customPragmaNode(n: NimNode): NimNode = let impl = n.getImpl() if impl.kind in RoutineNodes: return impl.pragma - elif impl.kind == nnkIdentDefs and impl[0].kind == nnkPragmaExpr: + elif impl.kind in {nnkIdentDefs, nnkConstDef} and impl[0].kind == nnkPragmaExpr: return impl[0][1] else: let timpl = typ.getImpl() @@ -1560,7 +1586,7 @@ proc customPragmaNode(n: NimNode): NimNode = while typDef != nil: typDef.expectKind(nnkTypeDef) let typ = typDef[2].extractTypeImpl() - typ.expectKind({nnkRefTy, nnkPtrTy, nnkObjectTy}) + if typ.kind notin {nnkRefTy, nnkPtrTy, nnkObjectTy}: break let isRef = typ.kind in {nnkRefTy, nnkPtrTy} if isRef and typ[0].kind in {nnkSym, nnkBracketExpr}: # defines ref type for another object(e.g. X = ref X) typDef = getImpl(typ[0]) @@ -1604,7 +1630,7 @@ macro hasCustomPragma*(n: typed, cp: typed{nkSym}): untyped = ## ## See also `getCustomPragmaVal`_. ## - ## .. code-block:: nim + ## ``` ## template myAttr() {.pragma.} ## type ## MyObj = object @@ -1615,6 +1641,7 @@ macro hasCustomPragma*(n: typed, cp: typed{nkSym}): untyped = ## var o: MyObj ## assert(o.myField.hasCustomPragma(myAttr)) ## assert(myProc.hasCustomPragma(myAttr)) + ## ``` let pragmaNode = customPragmaNode(n) for p in pragmaNode: if (p.kind == nnkSym and p == cp) or @@ -1628,7 +1655,7 @@ macro getCustomPragmaVal*(n: typed, cp: typed{nkSym}): untyped = ## ## See also `hasCustomPragma`_. ## - ## .. code-block:: nim + ## ``` ## template serializationKey(key: string) {.pragma.} ## type ## MyObj {.serializationKey: "mo".} = object @@ -1637,6 +1664,7 @@ macro getCustomPragmaVal*(n: typed, cp: typed{nkSym}): untyped = ## assert(o.myField.getCustomPragmaVal(serializationKey) == "mf") ## assert(o.getCustomPragmaVal(serializationKey) == "mo") ## assert(MyObj.getCustomPragmaVal(serializationKey) == "mo") + ## ``` result = nil let pragmaNode = customPragmaNode(n) for p in pragmaNode: @@ -1723,25 +1751,26 @@ proc extractDocCommentsAndRunnables*(n: NimNode): NimNode = ## runnableExamples in `a`, stopping at the first child that is neither. ## Example: ## - ## .. code-block:: nim - ## import std/macros - ## macro transf(a): untyped = - ## result = quote do: - ## proc fun2*() = discard - ## let header = extractDocCommentsAndRunnables(a.body) - ## # correct usage: rest is appended - ## result.body = header - ## result.body.add quote do: discard # just an example - ## # incorrect usage: nesting inside a nnkStmtList: - ## # result.body = quote do: (`header`; discard) - ## - ## proc fun*() {.transf.} = - ## ## first comment - ## runnableExamples: discard - ## runnableExamples: discard - ## ## last comment - ## discard # first statement after doc comments + runnableExamples - ## ## not docgen'd + ## ``` + ## import std/macros + ## macro transf(a): untyped = + ## result = quote do: + ## proc fun2*() = discard + ## let header = extractDocCommentsAndRunnables(a.body) + ## # correct usage: rest is appended + ## result.body = header + ## result.body.add quote do: discard # just an example + ## # incorrect usage: nesting inside a nnkStmtList: + ## # result.body = quote do: (`header`; discard) + ## + ## proc fun*() {.transf.} = + ## ## first comment + ## runnableExamples: discard + ## runnableExamples: discard + ## ## last comment + ## discard # first statement after doc comments + runnableExamples + ## ## not docgen'd + ## ``` result = newStmtList() for ni in n: diff --git a/lib/core/typeinfo.nim b/lib/core/typeinfo.nim index 18d2091b9fa9..89b1deb31f66 100644 --- a/lib/core/typeinfo.nim +++ b/lib/core/typeinfo.nim @@ -40,6 +40,10 @@ include "system/hti.nim" {.pop.} +when defined(nimPreviewSlimSystem): + import std/assertions + + type AnyKind* = enum ## The kind of `Any`. akNone = 0, ## invalid @@ -87,7 +91,7 @@ type rawTypePtr: pointer ppointer = ptr pointer - pbyteArray = ptr array[0xffff, int8] + pbyteArray = ptr array[0xffff, uint8] when not defined(gcDestructors): type @@ -135,10 +139,10 @@ proc getDiscriminant(aa: pointer, n: ptr TNimNode): int = var d: int let a = cast[ByteAddress](aa) case n.typ.size - 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)[]) + of 1: d = int(cast[ptr uint8](a +% n.offset)[]) + of 2: d = int(cast[ptr uint16](a +% n.offset)[]) + of 4: d = int(cast[ptr uint32](a +% n.offset)[]) + of 8: d = int(cast[ptr uint64](a +% n.offset)[]) else: assert(false) return d @@ -480,8 +484,8 @@ proc getBiggestInt*(x: Any): BiggestInt = of tyChar: result = BiggestInt(cast[ptr char](x.value)[]) of tyEnum, tySet: case t.size - of 1: result = ze64(cast[ptr int8](x.value)[]) - of 2: result = ze64(cast[ptr int16](x.value)[]) + of 1: result = int64(cast[ptr uint8](x.value)[]) + of 2: result = int64(cast[ptr uint16](x.value)[]) of 4: result = BiggestInt(cast[ptr int32](x.value)[]) of 8: result = BiggestInt(cast[ptr int64](x.value)[]) else: assert false @@ -505,8 +509,8 @@ proc setBiggestInt*(x: Any, y: BiggestInt) = of tyChar: cast[ptr char](x.value)[] = chr(y.int) of tyEnum, tySet: case t.size - of 1: cast[ptr int8](x.value)[] = toU8(y.int) - of 2: cast[ptr int16](x.value)[] = toU16(y.int) + of 1: cast[ptr uint8](x.value)[] = uint8(y.int) + of 2: cast[ptr uint16](x.value)[] = uint16(y.int) of 4: cast[ptr int32](x.value)[] = int32(y) of 8: cast[ptr int64](x.value)[] = y else: assert false @@ -687,14 +691,14 @@ iterator elements*(x: Any): int = # "typ.slots.len" field is for sets the "first" field var u: int64 case typ.size - of 1: u = ze64(cast[ptr int8](p)[]) - of 2: u = ze64(cast[ptr int16](p)[]) - of 4: u = ze64(cast[ptr int32](p)[]) + of 1: u = int64(cast[ptr uint8](p)[]) + of 2: u = int64(cast[ptr uint16](p)[]) + of 4: u = int64(cast[ptr uint32](p)[]) of 8: u = cast[ptr int64](p)[] else: let a = cast[pbyteArray](p) for i in 0 .. typ.size*8-1: - if (ze(a[i div 8]) and (1 shl (i mod 8))) != 0: + if (int(a[i div 8]) and (1 shl (i mod 8))) != 0: yield i + typ.node.len if typ.size <= 8: for i in 0..sizeof(int64)*8-1: @@ -723,4 +727,4 @@ proc inclSetElement*(x: Any, elem: int) = a[] = a[] or (1'i64 shl e) else: var a = cast[pbyteArray](p) - a[e shr 3] = toU8(a[e shr 3] or (1 shl (e and 7))) + a[e shr 3] = a[e shr 3] or uint8(1 shl (e and 7)) diff --git a/lib/deprecated/pure/LockFreeHash.nim b/lib/deprecated/pure/LockFreeHash.nim deleted file mode 100644 index 69e12226fe06..000000000000 --- a/lib/deprecated/pure/LockFreeHash.nim +++ /dev/null @@ -1,611 +0,0 @@ -#nim c -t:-march=i686 --cpu:amd64 --threads:on -d:release lockfreehash.nim - -#------------------------------------------------------------------------------ -## Memory Utility Functions - -{.deprecated.} - -import math, hashes - -proc newHeap*[T](): ptr T = - result = cast[ptr T](alloc0(sizeof(T))) - -proc copyNew*[T](x: var T): ptr T = - var - size = sizeof(T) - mem = alloc(size) - copyMem(mem, x.addr, size) - return cast[ptr T](mem) - -proc copyTo*[T](val: var T, dest: int) = - copyMem(pointer(dest), val.addr, sizeof(T)) - -proc allocType*[T](): pointer = alloc(sizeof(T)) - -proc newShared*[T](): ptr T = - result = cast[ptr T](allocShared0(sizeof(T))) - -proc copyShared*[T](x: var T): ptr T = - var - size = sizeof(T) - mem = allocShared(size) - copyMem(mem, x.addr, size) - return cast[ptr T](mem) - -#------------------------------------------------------------------------------ -## Pointer arithmetic - -proc `+`*(p: pointer, i: int): pointer {.inline.} = - cast[pointer](cast[int](p) + i) - -const - minTableSize = 8 - reProbeLimit = 12 - minCopyWork = 4096 - intSize = sizeof(int) - - - -when sizeof(int) == 4: # 32bit - type - Raw = range[0..1073741823] - ## The range of uint values that can be stored directly in a value slot - ## when on a 32 bit platform -elif sizeof(int) == 8: # 64bit - type - Raw = range[0'i64..4611686018427387903'i64] - ## The range of uint values that can be stored directly in a value slot - ## when on a 64 bit platform -else: - {.error: "unsupported platform".} - -type - Entry = tuple - key: int - value: int - - EntryArr = ptr array[0..10_000_000, Entry] - - PConcTable[K, V] = ptr object {.pure.} - len: int - used: int - active: int - copyIdx: int - copyDone: int - next: PConcTable[K, V] - data: EntryArr - -proc setVal[K, V](table: var PConcTable[K, V], key: int, val: int, - expVal: int, match: bool): int - -#------------------------------------------------------------------------------ - -# Create a new table -proc newLFTable*[K, V](size: int = minTableSize): PConcTable[K, V] = - let - dataLen = max(nextPowerOfTwo(size), minTableSize) - dataSize = dataLen*sizeof(Entry) - dataMem = allocShared0(dataSize) - tableSize = 7 * intSize - tableMem = allocShared0(tableSize) - table = cast[PConcTable[K, V]](tableMem) - table.len = dataLen - table.used = 0 - table.active = 0 - table.copyIdx = 0 - table.copyDone = 0 - table.next = nil - table.data = cast[EntryArr](dataMem) - result = table - -#------------------------------------------------------------------------------ - -# Delete a table -proc deleteConcTable[K, V](tbl: PConcTable[K, V]) = - deallocShared(tbl.data) - deallocShared(tbl) - -#------------------------------------------------------------------------------ - -proc `[]`[K, V](table: var PConcTable[K, V], i: int): var Entry {.inline.} = - table.data[i] - -#------------------------------------------------------------------------------ -# State flags stored in ptr - - -proc pack[T](x: T): int {.inline.} = - result = (cast[int](x) shl 2) - #echo("packKey ",cast[int](x) , " -> ", result) - -# Pop the flags off returning a 4 byte aligned ptr to our Key or Val -proc pop(x: int): int {.inline.} = - result = x and 0xFFFFFFFC'i32 - -# Pop the raw value off of our Key or Val -proc popRaw(x: int): int {.inline.} = - result = x shr 2 - -# Pop the flags off returning a 4 byte aligned ptr to our Key or Val -proc popPtr[V](x: int): ptr V {.inline.} = - result = cast[ptr V](pop(x)) - #echo("popPtr " & $x & " -> " & $cast[int](result)) - -# Ghost (sentinel) -# K or V is no longer valid use new table -const Ghost = 0xFFFFFFFC -proc isGhost(x: int): bool {.inline.} = - result = x == 0xFFFFFFFC - -# Tombstone -# applied to V = K is dead -proc isTomb(x: int): bool {.inline.} = - result = (x and 0x00000002) != 0 - -proc setTomb(x: int): int {.inline.} = - result = x or 0x00000002 - -# Prime -# K or V is in new table copied from old -proc isPrime(x: int): bool {.inline.} = - result = (x and 0x00000001) != 0 - -proc setPrime(x: int): int {.inline.} = - result = x or 0x00000001 - -#------------------------------------------------------------------------------ - -##This is for i32 only need to override for i64 -proc hashInt(x: int): int {.inline.} = - var h = uint32(x) #shr 2'u32 - h = h xor (h shr 16'u32) - h *= 0x85ebca6b'u32 - h = h xor (h shr 13'u32) - h *= 0xc2b2ae35'u32 - h = h xor (h shr 16'u32) - result = int(h) - -#------------------------------------------------------------------------------ - -proc resize[K, V](self: PConcTable[K, V]): PConcTable[K, V] = - var next = atomic_load_n(self.next.addr, ATOMIC_RELAXED) - #echo("next = " & $cast[int](next)) - if next != nil: - #echo("A new table already exists, copy in progress") - return next - var - oldLen = atomic_load_n(self.len.addr, ATOMIC_RELAXED) - newTable = newLFTable[K, V](oldLen*2) - success = atomic_compare_exchange_n(self.next.addr, next.addr, newTable, - false, ATOMIC_RELAXED, ATOMIC_RELAXED) - if not success: - echo("someone beat us to it! delete table we just created and return his " & - $cast[int](next)) - deleteConcTable(newTable) - return next - else: - echo("Created New Table! " & $cast[int](newTable) & " Size = " & $newTable.len) - return newTable - - -#------------------------------------------------------------------------------ -#proc keyEQ[K](key1: ptr K, key2: ptr K): bool {.inline.} = -proc keyEQ[K](key1: int, key2: int): bool {.inline.} = - result = false - when K is Raw: - if key1 == key2: - result = true - else: - var - p1 = popPtr[K](key1) - p2 = popPtr[K](key2) - if p1 != nil and p2 != nil: - if cast[int](p1) == cast[int](p2): - return true - if p1[] == p2[]: - return true - -#------------------------------------------------------------------------------ - -#proc tableFull(self: var PConcTable[K,V]) : bool {.inline.} = - - -#------------------------------------------------------------------------------ - -proc copySlot[K, V](idx: int, oldTbl: var PConcTable[K, V], - newTbl: var PConcTable[K, V]): bool = - #echo("Copy idx " & $idx) - var - oldVal = 0 - oldkey = 0 - ok = false - result = false - #Block the key so no other threads waste time here - while not ok: - ok = atomic_compare_exchange_n(oldTbl[idx].key.addr, oldKey.addr, - setTomb(oldKey), false, ATOMIC_RELAXED, ATOMIC_RELAXED) - #echo("oldKey was = " & $oldKey & " set it to tomb " & $setTomb(oldKey)) - #Prevent new values from appearing in the old table by priming - oldVal = atomic_load_n(oldTbl[idx].value.addr, ATOMIC_RELAXED) - while not isPrime(oldVal): - var box = if oldVal == 0 or isTomb(oldVal): oldVal.setTomb.setPrime - else: oldVal.setPrime - if atomic_compare_exchange_n(oldTbl[idx].value.addr, oldVal.addr, - box, false, ATOMIC_RELAXED, ATOMIC_RELAXED): - if isPrime(box) and isTomb(box): - return true - oldVal = box - break - #echo("oldVal was = ", oldVal, " set it to prime ", box) - if isPrime(oldVal) and isTomb(oldVal): - #when not (K is Raw): - # deallocShared(popPtr[K](oldKey)) - return false - if isTomb(oldVal): - echo("oldVal is Tomb!!!, should not happen") - if pop(oldVal) != 0: - result = setVal(newTbl, pop(oldKey), pop(oldVal), 0, true) == 0 - #if result: - #echo("Copied a Slot! idx= " & $idx & " key= " & $oldKey & " val= " & $oldVal) - #else: - #echo("copy slot failed") - # Our copy is done so we disable the old slot - while not ok: - ok = atomic_compare_exchange_n(oldTbl[idx].value.addr, oldVal.addr, - oldVal.setTomb.setPrime, false, ATOMIC_RELAXED, ATOMIC_RELAXED) - #echo("disabled old slot") - #echo"---------------------" - -#------------------------------------------------------------------------------ - -proc promote[K, V](table: var PConcTable[K, V]) = - var - newData = atomic_load_n(table.next.data.addr, ATOMIC_RELAXED) - newLen = atomic_load_n(table.next.len.addr, ATOMIC_RELAXED) - newUsed = atomic_load_n(table.next.used.addr, ATOMIC_RELAXED) - - deallocShared(table.data) - atomic_store_n(table.data.addr, newData, ATOMIC_RELAXED) - atomic_store_n(table.len.addr, newLen, ATOMIC_RELAXED) - atomic_store_n(table.used.addr, newUsed, ATOMIC_RELAXED) - atomic_store_n(table.copyIdx.addr, 0, ATOMIC_RELAXED) - atomic_store_n(table.copyDone.addr, 0, ATOMIC_RELAXED) - deallocShared(table.next) - atomic_store_n(table.next.addr, nil, ATOMIC_RELAXED) - echo("new table swapped!") - -#------------------------------------------------------------------------------ - -proc checkAndPromote[K, V](table: var PConcTable[K, V], workDone: int): bool = - var - oldLen = atomic_load_n(table.len.addr, ATOMIC_RELAXED) - copyDone = atomic_load_n(table.copyDone.addr, ATOMIC_RELAXED) - ok: bool - result = false - if workDone > 0: - #echo("len to copy =" & $oldLen) - #echo("copyDone + workDone = " & $copyDone & " + " & $workDone) - while not ok: - ok = atomic_compare_exchange_n(table.copyDone.addr, copyDone.addr, - copyDone + workDone, false, ATOMIC_RELAXED, ATOMIC_RELAXED) - #if ok: echo("set copyDone") - # If the copy is done we can promote this table - if copyDone + workDone >= oldLen: - # Swap new data - #echo("work is done!") - table.promote - result = true - -#------------------------------------------------------------------------------ - -proc copySlotAndCheck[K, V](table: var PConcTable[K, V], idx: int): - PConcTable[K, V] = - var - newTable = cast[PConcTable[K, V]](atomic_load_n(table.next.addr, - ATOMIC_RELAXED)) - result = newTable - if newTable != nil and copySlot(idx, table, newTable): - #echo("copied a single slot, idx = " & $idx) - if checkAndPromote(table, 1): return table - - -#------------------------------------------------------------------------------ - -proc helpCopy[K, V](table: var PConcTable[K, V]): PConcTable[K, V] = - var - newTable = cast[PConcTable[K, V]](atomic_load_n(table.next.addr, - ATOMIC_RELAXED)) - result = newTable - if newTable != nil: - var - oldLen = atomic_load_n(table.len.addr, ATOMIC_RELAXED) - copyDone = atomic_load_n(table.copyDone.addr, ATOMIC_RELAXED) - copyIdx = 0 - work = min(oldLen, minCopyWork) - #panicStart = -1 - workDone = 0 - if copyDone < oldLen: - var ok: bool - while not ok: - ok = atomic_compare_exchange_n(table.copyIdx.addr, copyIdx.addr, - copyIdx + work, false, ATOMIC_RELAXED, ATOMIC_RELAXED) - #echo("copy idx = ", copyIdx) - for i in 0..work-1: - var idx = (copyIdx + i) and (oldLen - 1) - if copySlot(idx, table, newTable): - workDone += 1 - if workDone > 0: - #echo("did work ", workDone, " on thread ", cast[int](myThreadID[pointer]())) - if checkAndPromote(table, workDone): return table - # In case a thread finished all the work then got stalled before promotion - if checkAndPromote(table, 0): return table - - - -#------------------------------------------------------------------------------ - -proc setVal[K, V](table: var PConcTable[K, V], key: int, val: int, - expVal: int, match: bool): int = - #echo("-try set- in table ", " key = ", (popPtr[K](key)[]), " val = ", val) - when K is Raw: - var idx = hashInt(key) - else: - var idx = popPtr[K](key)[].hash - var - nextTable: PConcTable[K, V] - probes = 1 - # spin until we find a key slot or build and jump to next table - while true: - idx = idx and (table.len - 1) - #echo("try set idx = " & $idx & "for" & $key) - var - probedKey = 0 - openKey = atomic_compare_exchange_n(table[idx].key.addr, probedKey.addr, - key, false, ATOMIC_RELAXED, ATOMIC_RELAXED) - if openKey: - if val.isTomb: - #echo("val was tomb, bail, no reason to set an open slot to tomb") - return val - #increment used slots - #echo("found an open slot, total used = " & - #$atomic_add_fetch(table.used.addr, 1, ATOMIC_RELAXED)) - discard atomic_add_fetch(table.used.addr, 1, ATOMIC_RELAXED) - break # We found an open slot - #echo("set idx ", idx, " key = ", key, " probed = ", probedKey) - if keyEQ[K](probedKey, key): - #echo("we found the matching slot") - break # We found a matching slot - if (not(expVal != 0 and match)) and (probes >= reProbeLimit or key.isTomb): - if key.isTomb: echo("Key is Tombstone") - #if probes >= reProbeLimit: echo("Too much probing " & $probes) - #echo("try to resize") - #create next bigger table - nextTable = resize(table) - #help do some copying - #echo("help copy old table to new") - nextTable = helpCopy(table) - #now setVal in the new table instead - #echo("jumping to next table to set val") - return setVal(nextTable, key, val, expVal, match) - else: - idx += 1 - probes += 1 - # Done spinning for a new slot - var oldVal = atomic_load_n(table[idx].value.addr, ATOMIC_RELAXED) - if val == oldVal: - #echo("this val is already in the slot") - return oldVal - nextTable = atomic_load_n(table.next.addr, ATOMIC_SEQ_CST) - if nextTable == nil and - ((oldVal == 0 and - (probes >= reProbeLimit or table.used / table.len > 0.8)) or - (isPrime(oldVal))): - if table.used / table.len > 0.8: echo("resize because usage ratio = " & - $(table.used / table.len)) - if isPrime(oldVal): echo("old val isPrime, should be a rare mem ordering event") - nextTable = resize(table) - if nextTable != nil: - #echo("tomb old slot then set in new table") - nextTable = copySlotAndCheck(table, idx) - return setVal(nextTable, key, val, expVal, match) - # Finally ready to add new val to table - while true: - if match and oldVal != expVal: - #echo("set failed, no match oldVal= " & $oldVal & " expVal= " & $expVal) - return oldVal - if atomic_compare_exchange_n(table[idx].value.addr, oldVal.addr, - val, false, ATOMIC_RELEASE, ATOMIC_RELAXED): - #echo("val set at table " & $cast[int](table)) - if expVal != 0: - if (oldVal == 0 or isTomb(oldVal)) and not isTomb(val): - discard atomic_add_fetch(table.active.addr, 1, ATOMIC_RELAXED) - elif not (oldVal == 0 or isTomb(oldVal)) and isTomb(val): - discard atomic_add_fetch(table.active.addr, -1, ATOMIC_RELAXED) - if oldVal == 0 and expVal != 0: - return setTomb(oldVal) - else: return oldVal - if isPrime(oldVal): - nextTable = copySlotAndCheck(table, idx) - return setVal(nextTable, key, val, expVal, match) - -#------------------------------------------------------------------------------ - -proc getVal[K, V](table: var PConcTable[K, V], key: int): int = - #echo("-try get- key = " & $key) - when K is Raw: - var idx = hashInt(key) - else: - var idx = popPtr[K](key)[].hash - #echo("get idx ", idx) - var - probes = 0 - val: int - while true: - idx = idx and (table.len - 1) - var - newTable: PConcTable[K, V] # = atomic_load_n(table.next.addr, ATOMIC_ACQUIRE) - probedKey = atomic_load_n(table[idx].key.addr, ATOMIC_SEQ_CST) - if keyEQ[K](probedKey, key): - #echo("found key after ", probes+1) - val = atomic_load_n(table[idx].value.addr, ATOMIC_ACQUIRE) - if not isPrime(val): - if isTomb(val): - #echo("val was tomb but not prime") - return 0 - else: - #echo("-GotIt- idx = ", idx, " key = ", key, " val ", val ) - return val - else: - newTable = copySlotAndCheck(table, idx) - return getVal(newTable, key) - else: - #echo("probe ", probes, " idx = ", idx, " key = ", key, " found ", probedKey ) - if probes >= reProbeLimit*4 or key.isTomb: - if newTable == nil: - #echo("too many probes and no new table ", key, " ", idx ) - return 0 - else: - newTable = helpCopy(table) - return getVal(newTable, key) - idx += 1 - probes += 1 - -#------------------------------------------------------------------------------ - -#proc set*(table: var PConcTable[Raw,Raw], key: Raw, val: Raw) = -# discard setVal(table, pack(key), pack(key), 0, false) - -#proc set*[V](table: var PConcTable[Raw,V], key: Raw, val: ptr V) = -# discard setVal(table, pack(key), cast[int](val), 0, false) - -proc set*[K, V](table: var PConcTable[K, V], key: var K, val: var V) = - when not (K is Raw): - var newKey = cast[int](copyShared(key)) - else: - var newKey = pack(key) - when not (V is Raw): - var newVal = cast[int](copyShared(val)) - else: - var newVal = pack(val) - var oldPtr = pop(setVal(table, newKey, newVal, 0, false)) - #echo("oldPtr = ", cast[int](oldPtr), " newPtr = ", cast[int](newPtr)) - when not (V is Raw): - if newVal != oldPtr and oldPtr != 0: - deallocShared(cast[ptr V](oldPtr)) - - - -proc get*[K, V](table: var PConcTable[K, V], key: var K): V = - when not (V is Raw): - when not (K is Raw): - return popPtr[V](getVal(table, cast[int](key.addr)))[] - else: - return popPtr[V](getVal(table, pack(key)))[] - else: - when not (K is Raw): - return popRaw(getVal(table, cast[int](key.addr))) - else: - return popRaw(getVal(table, pack(key))) - - - - - - - - - - - -#proc `[]`[K,V](table: var PConcTable[K,V], key: K): PEntry[K,V] {.inline.} = -# getVal(table, key) - -#proc `[]=`[K,V](table: var PConcTable[K,V], key: K, val: V): PEntry[K,V] {.inline.} = -# setVal(table, key, val) - - - - - - -#Tests ---------------------------- -when not defined(testing) and isMainModule: - import locks, times, mersenne - - const - numTests = 100000 - numThreads = 10 - - - - type - TestObj = tuple - thr: int - f0: int - f1: int - - Data = tuple[k: string, v: TestObj] - PDataArr = array[0..numTests-1, Data] - Dict = PConcTable[string, TestObj] - - var - thr: array[0..numThreads-1, Thread[Dict]] - - table = newLFTable[string, TestObj](8) - rand = newMersenneTwister(2525) - - proc createSampleData(len: int): PDataArr = - #result = cast[PDataArr](allocShared0(sizeof(Data)*numTests)) - for i in 0..len-1: - result[i].k = "mark" & $(i+1) - #echo("mark" & $(i+1), " ", hash("mark" & $(i+1))) - result[i].v.thr = 0 - result[i].v.f0 = i+1 - result[i].v.f1 = 0 - #echo("key = " & $(i+1) & " Val ptr = " & $cast[int](result[i].v.addr)) - - - - proc threadProc(tp: Dict) {.thread.} = - var t = cpuTime(); - for i in 1..numTests: - var key = "mark" & $(i) - var got = table.get(key) - got.thr = cast[int](myThreadID[pointer]()) - got.f1 = got.f1 + 1 - table.set(key, got) - t = cpuTime() - t - echo t - - - var testData = createSampleData(numTests) - - for i in 0..numTests-1: - table.set(testData[i].k, testData[i].v) - - var i = 0 - while i < numThreads: - createThread(thr[i], threadProc, table) - i += 1 - - joinThreads(thr) - - - - - - var fails = 0 - - for i in 0..numTests-1: - var got = table.get(testData[i].k) - if got.f0 != i+1 or got.f1 != numThreads: - fails += 1 - echo(got) - - echo("Failed read or write = ", fails) - - - #for i in 1..numTests: - # echo(i, " = ", hashInt(i) and 8191) - - deleteConcTable(table) diff --git a/lib/deprecated/pure/events.nim b/lib/deprecated/pure/events.nim deleted file mode 100644 index 85fc349c2443..000000000000 --- a/lib/deprecated/pure/events.nim +++ /dev/null @@ -1,104 +0,0 @@ -# -# -# Nim's Runtime Library -# (c) Copyright 2011 Alexander Mitchell-Robinson -# -# See the file "copying.txt", included in this -# distribution, for details about the copyright. -# - -## :Author: Alexander Mitchell-Robinson -## -## Unstable API. -## -## This module implements an event system that is not dependent on external -## graphical toolkits. It was originally called `NimEE` because -## it was inspired by Python's PyEE module. There are two ways you can use -## events: one is a python-inspired way; the other is more of a C-style way. -## -## .. code-block:: Nim -## var ee = initEventEmitter() -## var genericargs: EventArgs -## proc handleevent(e: EventArgs) = -## echo("Handled!") -## -## # Python way -## ee.on("EventName", handleevent) -## ee.emit("EventName", genericargs) -## -## # C/Java way -## # Declare a type -## type -## SomeObject = object of RootObj -## SomeEvent: EventHandler -## var myobj: SomeObject -## myobj.SomeEvent = initEventHandler("SomeEvent") -## myobj.SomeEvent.addHandler(handleevent) -## ee.emit(myobj.SomeEvent, genericargs) - -{.deprecated.} - -type - EventArgs* = object of RootObj ## Base object for event arguments that are passed to callback functions. - EventHandler* = tuple[name: string, handlers: seq[proc(e: EventArgs) {.closure.}]] ## An eventhandler for an event. - -type - EventEmitter* = object ## An object that fires events and holds event handlers for an object. - s: seq[EventHandler] - EventError* = object of ValueError - -proc initEventHandler*(name: string): EventHandler = - ## Initializes an EventHandler with the specified name and returns it. - result.handlers = @[] - result.name = name - -proc addHandler*(handler: var EventHandler, fn: proc(e: EventArgs) {.closure.}) = - ## Adds the callback to the specified event handler. - handler.handlers.add(fn) - -proc removeHandler*(handler: var EventHandler, fn: proc(e: EventArgs) {.closure.}) = - ## Removes the callback from the specified event handler. - for i in countup(0, len(handler.handlers)-1): - if fn == handler.handlers[i]: - handler.handlers.del(i) - break - -proc containsHandler*(handler: var EventHandler, fn: proc(e: EventArgs) {.closure.}): bool = - ## Checks if a callback is registered to this event handler. - return handler.handlers.contains(fn) - - -proc clearHandlers*(handler: var EventHandler) = - ## Clears all of the callbacks from the event handler. - setLen(handler.handlers, 0) - -proc getEventHandler(emitter: var EventEmitter, event: string): int = - for k in 0..high(emitter.s): - if emitter.s[k].name == event: return k - return -1 - -proc on*(emitter: var EventEmitter, event: string, fn: proc(e: EventArgs) {.closure.}) = - ## Assigns a event handler with the specified callback. If the event - ## doesn't exist, it will be created. - var i = getEventHandler(emitter, event) - if i < 0: - var eh = initEventHandler(event) - addHandler(eh, fn) - emitter.s.add(eh) - else: - addHandler(emitter.s[i], fn) - -proc emit*(emitter: var EventEmitter, eventhandler: var EventHandler, - args: EventArgs) = - ## Fires an event handler with specified event arguments. - for fn in items(eventhandler.handlers): fn(args) - -proc emit*(emitter: var EventEmitter, event: string, args: EventArgs) = - ## Fires an event handler with specified event arguments. - var i = getEventHandler(emitter, event) - if i >= 0: - emit(emitter, emitter.s[i], args) - -proc initEventEmitter*(): EventEmitter = - ## Creates and returns a new EventEmitter. - result.s = @[] diff --git a/lib/deprecated/pure/future.nim b/lib/deprecated/pure/future.nim new file mode 100644 index 000000000000..3f7b63a30db0 --- /dev/null +++ b/lib/deprecated/pure/future.nim @@ -0,0 +1,6 @@ +## This module is a deprecated alias for the `sugar` module. Deprecated since 0.19.0. + +{.deprecated: "Use the new 'sugar' module instead".} + +import sugar +export sugar diff --git a/lib/deprecated/pure/ospaths.nim b/lib/deprecated/pure/ospaths.nim index e253a3e5a019..b57839d1aa3f 100644 --- a/lib/deprecated/pure/ospaths.nim +++ b/lib/deprecated/pure/ospaths.nim @@ -7,7 +7,7 @@ # distribution, for details about the copyright. # -## This module is deprecated, `import std/os` instead. +## This module is deprecated since 0.20.0, `import std/os` instead. {.deprecated: "use `std/os` instead".} diff --git a/lib/deprecated/pure/parseopt2.nim b/lib/deprecated/pure/parseopt2.nim deleted file mode 100644 index a6d5de06df24..000000000000 --- a/lib/deprecated/pure/parseopt2.nim +++ /dev/null @@ -1,155 +0,0 @@ -# -# -# Nim's Runtime Library -# (c) Copyright 2015 Andreas Rumpf -# -# See the file "copying.txt", included in this -# distribution, for details about the copyright. -# - -## This module provides the standard Nim command line parser. -## It supports one convenience iterator over all command line options and some -## lower-level features. -## -## Supported syntax: -## -## 1. short options - `-abcd`, where a, b, c, d are names -## 2. long option - `--foo:bar`, `--foo=bar` or `--foo` -## 3. argument - everything else - -{.deprecated: "Use the 'parseopt' module instead".} -{.push debugger: off.} - -include "system/inclrtl" - -import - os, strutils - -type - CmdLineKind* = enum ## the detected command line token - cmdEnd, ## end of command line reached - cmdArgument, ## argument detected - cmdLongOption, ## a long option `--option` detected - cmdShortOption ## a short option `-c` detected - OptParser* = - object of RootObj ## this object implements the command line parser - cmd: seq[string] - pos: int - remainingShortOptions: string - kind*: CmdLineKind ## the detected command line token - key*, val*: string ## key and value pair; `key` is the option - ## or the argument, `value` is not "" if - ## the option was given a value - -proc initOptParser*(cmdline: seq[string]): OptParser {.rtl.} = - ## Initializes option parses with cmdline. cmdline should not contain - ## argument 0 - program name. - ## If cmdline.len == 0 default to current command line arguments. - result.remainingShortOptions = "" - when not defined(createNimRtl): - if cmdline.len == 0: - result.cmd = commandLineParams() - return - else: - assert cmdline != nil, "Cannot determine command line arguments." - - result.cmd = @cmdline - -when not defined(createNimRtl): - proc initOptParser*(): OptParser = - ## Initializes option parser from current command line arguments. - return initOptParser(commandLineParams()) - -proc next*(p: var OptParser) {.rtl, extern: "npo2$1".} - -proc nextOption(p: var OptParser, token: string, allowEmpty: bool) = - for splitchar in [':', '=']: - if splitchar in token: - let pos = token.find(splitchar) - p.key = token[0..pos-1] - p.val = token[pos+1..token.len-1] - return - - p.key = token - if allowEmpty: - p.val = "" - else: - p.remainingShortOptions = token[0..token.len-1] - p.next() - -proc next(p: var OptParser) = - if p.remainingShortOptions.len != 0: - p.kind = cmdShortOption - p.key = p.remainingShortOptions[0..0] - p.val = "" - p.remainingShortOptions = p.remainingShortOptions[1..p.remainingShortOptions.len-1] - return - - if p.pos >= p.cmd.len: - p.kind = cmdEnd - return - - let token = p.cmd[p.pos] - p.pos += 1 - - if token.startsWith("--"): - p.kind = cmdLongOption - nextOption(p, token[2..token.len-1], allowEmpty=true) - elif token.startsWith("-"): - p.kind = cmdShortOption - nextOption(p, token[1..token.len-1], allowEmpty=true) - else: - p.kind = cmdArgument - p.key = token - p.val = "" - -proc cmdLineRest*(p: OptParser): string {.rtl, extern: "npo2$1".} = - ## Returns the part of command line string that has not been parsed yet, - ## properly quoted. - return p.cmd[p.pos..p.cmd.len-1].quoteShellCommand - -type - GetoptResult* = tuple[kind: CmdLineKind, key, val: string] - -iterator getopt*(p: var OptParser): GetoptResult = - ## This is an convenience iterator for iterating over the given OptParser object. - ## Example: - ## - ## .. code-block:: nim - ## var p = initOptParser("--left --debug:3 -l=4 -r:2") - ## for kind, key, val in p.getopt(): - ## case kind - ## of cmdArgument: - ## filename = key - ## of cmdLongOption, cmdShortOption: - ## case key - ## of "help", "h": writeHelp() - ## of "version", "v": writeVersion() - ## of cmdEnd: assert(false) # cannot happen - ## if filename == "": - ## # no filename has been given, so we show the help: - ## writeHelp() - p.pos = 0 - while true: - next(p) - if p.kind == cmdEnd: break - yield (p.kind, p.key, p.val) - -when declared(paramCount): - iterator getopt*(): GetoptResult = - ## This is an convenience iterator for iterating over the command line arguments. - ## This create a new OptParser object. - ## See above for a more detailed example - ## - ## .. code-block:: nim - ## for kind, key, val in getopt(): - ## # this will iterate over all arguments passed to the cmdline. - ## continue - ## - var p = initOptParser() - while true: - next(p) - if p.kind == cmdEnd: break - yield (p.kind, p.key, p.val) - -{.pop.} diff --git a/lib/deprecated/pure/securehash.nim b/lib/deprecated/pure/securehash.nim index ea7930f8112b..b4749ad7589a 100644 --- a/lib/deprecated/pure/securehash.nim +++ b/lib/deprecated/pure/securehash.nim @@ -1,5 +1,6 @@ -## This module is a deprecated alias for the `sha1` module. +## This module is a deprecated alias for the `sha1` module. Deprecated since 0.18.1. {.deprecated: "use `std/sha1` instead".} -include "../std/sha1" +import "../std/sha1" +export sha1 diff --git a/lib/experimental/diff.nim b/lib/experimental/diff.nim index ea0a5cfb841e..4ca5eb31977c 100644 --- a/lib/experimental/diff.nim +++ b/lib/experimental/diff.nim @@ -45,6 +45,9 @@ jkl""" import tables, strutils +when defined(nimPreviewSlimSystem): + import std/assertions + type Item* = object ## An Item in the list of differences. startA*: int ## Start Line number in Data A. diff --git a/lib/impure/db_mysql.nim b/lib/impure/db_mysql.nim index df878e25af4c..279aebda5e62 100644 --- a/lib/impure/db_mysql.nim +++ b/lib/impure/db_mysql.nim @@ -19,10 +19,9 @@ ## All `db_*` modules support the same form of parameter substitution. ## That is, using the `?` (question mark) to signify the place where a ## value should be placed. For example: -## -## .. code-block:: Nim -## sql"INSERT INTO myTable (colA, colB, colC) VALUES (?, ?, ?)" -## +## ``` +## sql"INSERT INTO myTable (colA, colB, colC) VALUES (?, ?, ?)" +## ``` ## ## Examples ## ======== @@ -30,57 +29,60 @@ ## Opening a connection to a database ## ---------------------------------- ## -## .. code-block:: Nim -## import std/db_mysql -## let db = open("localhost", "user", "password", "dbname") -## db.close() +## ``` +## import std/db_mysql +## let db = open("localhost", "user", "password", "dbname") +## db.close() +## ``` ## ## Creating a table ## ---------------- ## -## .. code-block:: Nim -## db.exec(sql"DROP TABLE IF EXISTS myTable") -## db.exec(sql("""CREATE TABLE myTable ( -## id integer, -## name varchar(50) not null)""")) +## ``` +## db.exec(sql"DROP TABLE IF EXISTS myTable") +## db.exec(sql("""CREATE TABLE myTable ( +## id integer, +## name varchar(50) not null)""")) +## ``` ## ## Inserting data ## -------------- ## -## .. code-block:: Nim -## db.exec(sql"INSERT INTO myTable (id, name) VALUES (0, ?)", -## "Dominik") +## ``` +## db.exec(sql"INSERT INTO myTable (id, name) VALUES (0, ?)", +## "Dominik") +## ``` ## ## Larger example ## -------------- ## -## .. code-block:: Nim -## -## import std/[db_mysql, math] +## ``` +## import std/[db_mysql, math] ## -## let theDb = open("localhost", "nim", "nim", "test") +## let theDb = open("localhost", "nim", "nim", "test") ## -## theDb.exec(sql"Drop table if exists myTestTbl") -## theDb.exec(sql("create table myTestTbl (" & -## " Id INT(11) NOT NULL AUTO_INCREMENT PRIMARY KEY, " & -## " Name VARCHAR(50) NOT NULL, " & -## " i INT(11), " & -## " f DECIMAL(18,10))")) +## theDb.exec(sql"Drop table if exists myTestTbl") +## theDb.exec(sql("create table myTestTbl (" & +## " Id INT(11) NOT NULL AUTO_INCREMENT PRIMARY KEY, " & +## " Name VARCHAR(50) NOT NULL, " & +## " i INT(11), " & +## " f DECIMAL(18,10))")) ## -## theDb.exec(sql"START TRANSACTION") -## for i in 1..1000: -## theDb.exec(sql"INSERT INTO myTestTbl (name,i,f) VALUES (?,?,?)", -## "Item#" & $i, i, sqrt(i.float)) -## theDb.exec(sql"COMMIT") +## theDb.exec(sql"START TRANSACTION") +## for i in 1..1000: +## theDb.exec(sql"INSERT INTO myTestTbl (name,i,f) VALUES (?,?,?)", +## "Item#" & $i, i, sqrt(i.float)) +## theDb.exec(sql"COMMIT") ## -## for x in theDb.fastRows(sql"select * from myTestTbl"): -## echo x +## for x in theDb.fastRows(sql"select * from myTestTbl"): +## echo x ## -## let id = theDb.tryInsertId(sql"INSERT INTO myTestTbl (name,i,f) VALUES (?,?,?)", -## "Item#1001", 1001, sqrt(1001.0)) -## echo "Inserted item: ", theDb.getValue(sql"SELECT name FROM myTestTbl WHERE id=?", id) +## let id = theDb.tryInsertId(sql"INSERT INTO myTestTbl (name,i,f) VALUES (?,?,?)", +## "Item#1001", 1001, sqrt(1001.0)) +## echo "Inserted item: ", theDb.getValue(sql"SELECT name FROM myTestTbl WHERE id=?", id) ## -## theDb.close() +## theDb.close() +## ``` import strutils, mysql @@ -88,7 +90,7 @@ import strutils, mysql import db_common export db_common -import std/private/since +import std/private/[since, dbutils] type DbConn* = distinct PMySQL ## encapsulates a database connection @@ -117,7 +119,7 @@ when false: discard mysql_stmt_close(stmt) proc dbQuote*(s: string): string = - ## DB quotes the string. + ## DB quotes the string. Note that this doesn't escape `%` and `_`. result = newStringOfCap(s.len + 2) result.add "'" for c in items(s): @@ -132,35 +134,27 @@ proc dbQuote*(s: string): string = of '"': result.add "\\\"" of '\'': result.add "\\'" of '\\': result.add "\\\\" - of '_': result.add "\\_" else: result.add c add(result, '\'') proc dbFormat(formatstr: SqlQuery, args: varargs[string]): string = - result = "" - var a = 0 - for c in items(string(formatstr)): - if c == '?': - add(result, dbQuote(args[a])) - inc(a) - else: - add(result, c) + dbFormatImpl(formatstr, dbQuote, args) proc tryExec*(db: DbConn, query: SqlQuery, args: varargs[string, `$`]): bool {. tags: [ReadDbEffect, WriteDbEffect].} = ## tries to execute the query and returns true if successful, false otherwise. var q = dbFormat(query, args) - return mysql.real_query(PMySQL db, q, q.len) == 0'i32 + return mysql.real_query(PMySQL db, q.cstring, q.len) == 0'i32 proc rawExec(db: DbConn, query: SqlQuery, args: varargs[string, `$`]) = var q = dbFormat(query, args) - if mysql.real_query(PMySQL db, q, q.len) != 0'i32: dbError(db) + if mysql.real_query(PMySQL db, q.cstring, q.len) != 0'i32: dbError(db) proc exec*(db: DbConn, query: SqlQuery, args: varargs[string, `$`]) {. tags: [ReadDbEffect, WriteDbEffect].} = ## executes the query and raises EDB if not successful. var q = dbFormat(query, args) - if mysql.real_query(PMySQL db, q, q.len) != 0'i32: dbError(db) + if mysql.real_query(PMySQL db, q.cstring, q.len) != 0'i32: dbError(db) proc newRow(L: int): Row = newSeq(result, L) @@ -179,7 +173,7 @@ iterator fastRows*(db: DbConn, query: SqlQuery, ## if you require **ALL** the rows. ## ## Breaking the fastRows() iterator during a loop will cause the next - ## database query to raise an [EDb] exception `Commands out of sync`. + ## database query to raise an `EDb` exception `Commands out of sync`. rawExec(db, query, args) var sqlres = mysql.useResult(PMySQL db) if sqlres != nil: @@ -202,7 +196,7 @@ iterator instantRows*(db: DbConn, query: SqlQuery, args: varargs[string, `$`]): InstantRow {.tags: [ReadDbEffect].} = ## Same as fastRows but returns a handle that can be used to get column text - ## on demand using []. Returned handle is valid only within the iterator body. + ## on demand using `[]`. Returned handle is valid only within the iterator body. rawExec(db, query, args) var sqlres = mysql.useResult(PMySQL db) if sqlres != nil: @@ -282,7 +276,7 @@ proc setColumnInfo(columns: var DbColumns; res: PRES; L: int) = iterator instantRows*(db: DbConn; columns: var DbColumns; query: SqlQuery; args: varargs[string, `$`]): InstantRow = ## Same as fastRows but returns a handle that can be used to get column text - ## on demand using []. Returned handle is valid only within the iterator body. + ## on demand using `[]`. Returned handle is valid only within the iterator body. rawExec(db, query, args) var sqlres = mysql.useResult(PMySQL db) if sqlres != nil: @@ -357,11 +351,11 @@ proc getValue*(db: DbConn, query: SqlQuery, result = getRow(db, query, args)[0] proc tryInsertId*(db: DbConn, query: SqlQuery, - args: varargs[string, `$`]): int64 {.tags: [WriteDbEffect].} = + args: varargs[string, `$`]): int64 {.tags: [WriteDbEffect], raises: [DbError].} = ## executes the query (typically "INSERT") and returns the ## generated ID for the row or -1 in case of an error. var q = dbFormat(query, args) - if mysql.real_query(PMySQL db, q, q.len) != 0'i32: + if mysql.real_query(PMySQL db, q.cstring, q.len) != 0'i32: result = -1'i64 else: result = mysql.insertId(PMySQL db) @@ -375,7 +369,7 @@ proc insertId*(db: DbConn, query: SqlQuery, proc tryInsert*(db: DbConn, query: SqlQuery, pkName: string, args: varargs[string, `$`]): int64 - {.tags: [WriteDbEffect], raises: [], since: (1, 3).} = + {.tags: [WriteDbEffect], raises: [DbError], since: (1, 3).} = ## same as tryInsertID tryInsertID(db, query, args) @@ -410,7 +404,7 @@ proc open*(connection, user, password, database: string): DbConn {. else: substr(connection, 0, colonPos-1) port: int32 = if colonPos < 0: 0'i32 else: substr(connection, colonPos+1).parseInt.int32 - if mysql.realConnect(res, host, user, password, database, + if mysql.realConnect(res, host.cstring, user, password, database, port, nil, 0) == nil: var errmsg = $mysql.error(res) mysql.close(res) diff --git a/lib/impure/db_odbc.nim b/lib/impure/db_odbc.nim index 1e4032b34811..756957acb4d1 100644 --- a/lib/impure/db_odbc.nim +++ b/lib/impure/db_odbc.nim @@ -92,7 +92,7 @@ import strutils, odbcsql import db_common export db_common -import std/private/since +import std/private/[since, dbutils] type OdbcConnTyp = tuple[hDb: SqlHDBC, env: SqlHEnv, stmt: SqlHStmt] @@ -168,7 +168,7 @@ proc dbError*(db: var DbConn) {. raise e proc sqlCheck(db: var DbConn, resVal: TSqlSmallInt) {.raises: [DbError]} = - ## Wrapper that raises [EDb] if `resVal` is neither SQL_SUCCESS or SQL_NO_DATA + ## Wrapper that raises `EDb` if `resVal` is neither SQL_SUCCESS or SQL_NO_DATA if resVal notIn [SQL_SUCCESS, SQL_NO_DATA]: dbError(db) proc sqlGetDBMS(db: var DbConn): string {. @@ -197,14 +197,7 @@ proc dbFormat(formatstr: SqlQuery, args: varargs[string]): string {. noSideEffect.} = ## Replace any `?` placeholders with `args`, ## and quotes the arguments - result = "" - var a = 0 - for c in items(string(formatstr)): - if c == '?': - add(result, dbQuote(args[a])) - inc(a) - else: - add(result, c) + dbFormatImpl(formatstr, dbQuote, args) proc prepareFetch(db: var DbConn, query: SqlQuery, args: varargs[string, `$`]): TSqlSmallInt {. @@ -304,7 +297,7 @@ iterator instantRows*(db: var DbConn, query: SqlQuery, args: varargs[string, `$`]): InstantRow {.tags: [ReadDbEffect, WriteDbEffect].} = ## Same as fastRows but returns a handle that can be used to get column text - ## on demand using []. Returned handle is valid only within the iterator body. + ## on demand using `[]`. Returned handle is valid only within the iterator body. var rowRes: Row = @[] sz: TSqlLen = 0 @@ -334,7 +327,7 @@ proc `[]`*(row: InstantRow, col: int): string {.inline.} = proc unsafeColumnAt*(row: InstantRow, index: int): cstring {.inline.} = ## Return cstring of given column of the row - row.row[index] + row.row[index].cstring proc len*(row: InstantRow): int {.inline.} = ## Returns number of columns in the row diff --git a/lib/impure/db_postgres.nim b/lib/impure/db_postgres.nim index 36e035d3d217..ef530c605456 100644 --- a/lib/impure/db_postgres.nim +++ b/lib/impure/db_postgres.nim @@ -88,7 +88,7 @@ import strutils, postgres import db_common export db_common -import std/private/since +import std/private/[since, dbutils] type DbConn* = PPGconn ## encapsulates a database connection @@ -116,24 +116,12 @@ proc dbQuote*(s: string): string = add(result, '\'') proc dbFormat(formatstr: SqlQuery, args: varargs[string]): string = - result = "" - var a = 0 - if args.len > 0 and not string(formatstr).contains("?"): - dbError("""parameter substitution expects "?" """) - if args.len == 0: - return string(formatstr) - else: - for c in items(string(formatstr)): - if c == '?': - add(result, dbQuote(args[a])) - inc(a) - else: - add(result, c) + dbFormatImpl(formatstr, dbQuote, args) proc tryExec*(db: DbConn, query: SqlQuery, args: varargs[string, `$`]): bool {.tags: [ReadDbEffect, WriteDbEffect].} = ## tries to execute the query and returns true if successful, false otherwise. - var res = pqexecParams(db, dbFormat(query, args), 0, nil, nil, + var res = pqexecParams(db, dbFormat(query, args).cstring, 0, nil, nil, nil, nil, 0) result = pqresultStatus(res) == PGRES_COMMAND_OK pqclear(res) @@ -143,7 +131,7 @@ proc tryExec*(db: DbConn, stmtName: SqlPrepared, ReadDbEffect, WriteDbEffect].} = ## tries to execute the query and returns true if successful, false otherwise. var arr = allocCStringArray(args) - var res = pqexecPrepared(db, stmtName.string, int32(args.len), arr, + var res = pqexecPrepared(db, stmtName.cstring, int32(args.len), arr, nil, nil, 0) deallocCStringArray(arr) result = pqresultStatus(res) == PGRES_COMMAND_OK @@ -152,7 +140,7 @@ proc tryExec*(db: DbConn, stmtName: SqlPrepared, proc exec*(db: DbConn, query: SqlQuery, args: varargs[string, `$`]) {. tags: [ReadDbEffect, WriteDbEffect].} = ## executes the query and raises EDB if not successful. - var res = pqexecParams(db, dbFormat(query, args), 0, nil, nil, + var res = pqexecParams(db, dbFormat(query, args).cstring, 0, nil, nil, nil, nil, 0) if pqresultStatus(res) != PGRES_COMMAND_OK: dbError(db) pqclear(res) @@ -160,7 +148,7 @@ proc exec*(db: DbConn, query: SqlQuery, args: varargs[string, `$`]) {. proc exec*(db: DbConn, stmtName: SqlPrepared, args: varargs[string]) {.tags: [ReadDbEffect, WriteDbEffect].} = var arr = allocCStringArray(args) - var res = pqexecPrepared(db, stmtName.string, int32(args.len), arr, + var res = pqexecPrepared(db, stmtName.cstring, int32(args.len), arr, nil, nil, 0) deallocCStringArray(arr) if pqResultStatus(res) != PGRES_COMMAND_OK: dbError(db) @@ -172,20 +160,20 @@ proc newRow(L: int): Row = proc setupQuery(db: DbConn, query: SqlQuery, args: varargs[string]): PPGresult = - result = pqexec(db, dbFormat(query, args)) + result = pqexec(db, dbFormat(query, args).cstring) if pqResultStatus(result) != PGRES_TUPLES_OK: dbError(db) proc setupQuery(db: DbConn, stmtName: SqlPrepared, args: varargs[string]): PPGresult = var arr = allocCStringArray(args) - result = pqexecPrepared(db, stmtName.string, int32(args.len), arr, + result = pqexecPrepared(db, stmtName.cstring, int32(args.len), arr, nil, nil, 0) deallocCStringArray(arr) if pqResultStatus(result) != PGRES_TUPLES_OK: dbError(db) proc setupSingeRowQuery(db: DbConn, query: SqlQuery, args: varargs[string]) = - if pqsendquery(db, dbFormat(query, args)) != 1: + if pqsendquery(db, dbFormat(query, args).cstring) != 1: dbError(db) if pqSetSingleRowMode(db) != 1: dbError(db) @@ -193,7 +181,7 @@ proc setupSingeRowQuery(db: DbConn, query: SqlQuery, proc setupSingeRowQuery(db: DbConn, stmtName: SqlPrepared, args: varargs[string]) = var arr = allocCStringArray(args) - if pqsendqueryprepared(db, stmtName.string, int32(args.len), arr, nil, nil, 0) != 1: + if pqsendqueryprepared(db, stmtName.cstring, int32(args.len), arr, nil, nil, 0) != 1: dbError(db) if pqSetSingleRowMode(db) != 1: dbError(db) @@ -205,7 +193,7 @@ proc prepare*(db: DbConn; stmtName: string, query: SqlQuery; ## via `$1`, `$2`, `$3`, etc. if nParams > 0 and not string(query).contains("$1"): dbError("parameter substitution expects \"$1\"") - var res = pqprepare(db, stmtName, query.string, int32(nParams), nil) + var res = pqprepare(db, stmtName, query.cstring, int32(nParams), nil) if pqResultStatus(res) != PGRES_COMMAND_OK: dbError(db) return SqlPrepared(stmtName) @@ -271,7 +259,7 @@ iterator instantRows*(db: DbConn, query: SqlQuery, args: varargs[string, `$`]): InstantRow {.tags: [ReadDbEffect].} = ## same as fastRows but returns a handle that can be used to get column text - ## on demand using []. Returned handle is valid only within iterator body. + ## on demand using `[]`. Returned handle is valid only within iterator body. setupSingeRowQuery(db, query, args) fetchinstantRows(db) @@ -279,7 +267,7 @@ iterator instantRows*(db: DbConn, stmtName: SqlPrepared, args: varargs[string, `$`]): InstantRow {.tags: [ReadDbEffect].} = ## same as fastRows but returns a handle that can be used to get column text - ## on demand using []. Returned handle is valid only within iterator body. + ## on demand using `[]`. Returned handle is valid only within iterator body. setupSingeRowQuery(db, stmtName, args) fetchinstantRows(db) @@ -590,7 +578,7 @@ proc execAffectedRows*(db: DbConn, query: SqlQuery, ## executes the query (typically "UPDATE") and returns the ## number of affected rows. var q = dbFormat(query, args) - var res = pqExec(db, q) + var res = pqExec(db, q.cstring) if pqresultStatus(res) != PGRES_COMMAND_OK: dbError(db) result = parseBiggestInt($pqcmdTuples(res)) pqclear(res) @@ -601,7 +589,7 @@ proc execAffectedRows*(db: DbConn, stmtName: SqlPrepared, ## executes the query (typically "UPDATE") and returns the ## number of affected rows. var arr = allocCStringArray(args) - var res = pqexecPrepared(db, stmtName.string, int32(args.len), arr, + var res = pqexecPrepared(db, stmtName.cstring, int32(args.len), arr, nil, nil, 0) deallocCStringArray(arr) if pqresultStatus(res) != PGRES_COMMAND_OK: dbError(db) @@ -634,7 +622,7 @@ proc open*(connection, user, password, database: string): DbConn {. else: substr(connection, 0, colonPos-1) port = if colonPos < 0: "" else: substr(connection, colonPos+1) - result = pqsetdbLogin(host, port, nil, nil, database, user, password) + result = pqsetdbLogin(host.cstring, port.cstring, nil, nil, database, user, password) if pqStatus(result) != CONNECTION_OK: dbError(result) # result = nil proc setEncoding*(connection: DbConn, encoding: string): bool {. diff --git a/lib/impure/db_sqlite.nim b/lib/impure/db_sqlite.nim index 1638d38c625f..5e648d097c18 100644 --- a/lib/impure/db_sqlite.nim +++ b/lib/impure/db_sqlite.nim @@ -172,6 +172,8 @@ import db_common export db_common import std/private/[since, dbutils] +when defined(nimPreviewSlimSystem): + import std/assertions type DbConn* = PSqlite3 ## Encapsulates a database connection. @@ -235,7 +237,7 @@ proc tryExec*(db: DbConn, query: SqlQuery, assert(not db.isNil, "Database not connected.") var q = dbFormat(query, args) var stmt: sqlite3.PStmt - if prepare_v2(db, q, q.len.cint, stmt, nil) == SQLITE_OK: + if prepare_v2(db, q.cstring, q.len.cint, stmt, nil) == SQLITE_OK: let x = step(stmt) if x in {SQLITE_DONE, SQLITE_ROW}: result = finalize(stmt) == SQLITE_OK @@ -278,7 +280,7 @@ proc setupQuery(db: DbConn, query: SqlQuery, args: varargs[string]): PStmt = assert(not db.isNil, "Database not connected.") var q = dbFormat(query, args) - if prepare_v2(db, q, q.len.cint, result, nil) != SQLITE_OK: dbError(db) + if prepare_v2(db, q.cstring, q.len.cint, result, nil) != SQLITE_OK: dbError(db) proc setupQuery(db: DbConn, stmtName: SqlPrepared): SqlPrepared {.since: (1, 3).} = assert(not db.isNil, "Database not connected.") @@ -653,7 +655,7 @@ proc tryInsertID*(db: DbConn, query: SqlQuery, var q = dbFormat(query, args) var stmt: sqlite3.PStmt result = -1 - if prepare_v2(db, q, q.len.cint, stmt, nil) == SQLITE_OK: + if prepare_v2(db, q.cstring, q.len.cint, stmt, nil) == SQLITE_OK: if step(stmt) == SQLITE_DONE: result = last_insert_rowid(db) if finalize(stmt) != SQLITE_OK: diff --git a/lib/impure/nre.nim b/lib/impure/nre.nim index 03cbf5220ebe..e9dd49df08e6 100644 --- a/lib/impure/nre.nim +++ b/lib/impure/nre.nim @@ -66,6 +66,9 @@ from strutils import `%` import options from unicode import runeLenAt +when defined(nimPreviewSlimSystem): + import std/assertions + export options type @@ -75,16 +78,16 @@ type ## comment".` ## ## `pattern: string` - ## the string that was used to create the pattern. For details on how + ## : the string that was used to create the pattern. For details on how ## to write a pattern, please see `the official PCRE pattern ## documentation. ## `_ ## ## `captureCount: int` - ## the number of captures that the pattern has. + ## : the number of captures that the pattern has. ## ## `captureNameId: Table[string, int]` - ## a table from the capture names to their numeric id. + ## : a table from the capture names to their numeric id. ## ## ## Options @@ -151,36 +154,36 @@ type ## execution. On failure, it is none, on success, it is some. ## ## `pattern: Regex` - ## the pattern that is being matched + ## : the pattern that is being matched ## ## `str: string` - ## the string that was matched against + ## : the string that was matched against ## ## `captures[]: string` - ## the string value of whatever was captured at that id. If the value + ## : the string value of whatever was captured at that id. If the value ## is invalid, then behavior is undefined. If the id is `-1`, then ## the whole match is returned. If the given capture was not matched, ## `nil` is returned. See examples for `match`. ## ## `captureBounds[]: HSlice[int, int]` - ## gets the bounds of the given capture according to the same rules as + ## : gets the bounds of the given capture according to the same rules as ## the above. If the capture is not filled, then `None` is returned. ## The bounds are both inclusive. See examples for `match`. ## ## `match: string` - ## the full text of the match. + ## : the full text of the match. ## ## `matchBounds: HSlice[int, int]` - ## the bounds of the match, as in `captureBounds[]` + ## : the bounds of the match, as in `captureBounds[]` ## ## `(captureBounds|captures).toTable` - ## returns a table with each named capture as a key. + ## : returns a table with each named capture as a key. ## ## `(captureBounds|captures).toSeq` - ## returns all the captures by their number. + ## : returns all the captures by their number. ## ## `$: string` - ## same as `match` + ## : same as `match` pattern*: Regex ## The regex doing the matching. ## Not nil. str*: string ## The string that was matched against. @@ -583,11 +586,11 @@ proc find*(str: string, pattern: Regex, start = 0, endpos = int.high): Option[Re ## positions. ## ## `start` - ## The start point at which to start matching. `|abc` is `0`; + ## : The start point at which to start matching. `|abc` is `0`; ## `a|bc` is `1` ## ## `endpos` - ## The maximum index for a match; `int.high` means the end of the + ## : The maximum index for a match; `int.high` means the end of the ## string, otherwise it’s an inclusive upper bound. return str.matchImpl(pattern, start, endpos, 0) @@ -695,8 +698,7 @@ proc replace*(str: string, pattern: Regex, ## each match and the return value is the replacement value. ## ## If `subproc` is a `proc (string): string`, then it is executed with the - ## full text of the match and and the return value is the replacement - ## value. + ## full text of the match and the return value is the replacement value. ## ## If `subproc` is a string, the syntax is as follows: ## diff --git a/lib/impure/re.nim b/lib/impure/re.nim index 4eacf0091cf6..8b9de0c68ed1 100644 --- a/lib/impure/re.nim +++ b/lib/impure/re.nim @@ -34,6 +34,9 @@ runnableExamples: import pcre, strutils, rtarrays +when defined(nimPreviewSlimSystem): + import std/syncio + const MaxSubpatterns* = 20 ## defines the maximum number of subpatterns that can be captured. diff --git a/lib/js/dom.nim b/lib/js/dom.nim index c1a6772580c7..9859e95aee12 100644 --- a/lib/js/dom.nim +++ b/lib/js/dom.nim @@ -216,11 +216,13 @@ type defaultCharset*: cstring fgColor*: cstring head*: Element + hidden*: bool lastModified*: cstring linkColor*: cstring referrer*: cstring title*: cstring URL*: cstring + visibilityState*: cstring vlinkColor*: cstring anchors*: seq[AnchorElement] forms*: seq[FormElement] @@ -1780,3 +1782,60 @@ since (1, 3): since (1, 5): proc elementsFromPoint*(n: DocumentOrShadowRoot; x, y: float): seq[Element] {.importcpp.} + + +since (1, 7): + + proc insertAdjacentText*(self: Node; position, data: cstring) {.importjs: "#.$1(#, #)".} + ## https://developer.mozilla.org/en-US/docs/Web/API/Element/insertAdjacentText + + proc insertAdjacentElement*(self: Node; position: cstring; element: Node) {.importjs: "#.$1(#, #)".} + ## https://developer.mozilla.org/en-US/docs/Web/API/Element/insertAdjacentElement + + proc insertAdjacentHTML*(self: Node; position, html: cstring) {.importjs: "#.$1(#, #)".} + ## https://developer.mozilla.org/en-US/docs/Web/API/Element/insertAdjacentHTML + + proc after*(self: Node; element: Node): Node {.importjs: "#.$1(@)", varargs.} + ## https://developer.mozilla.org/en-US/docs/Web/API/Element/after + + proc before*(self: Node; element: Node): Node {.importjs: "#.$1(@)", varargs.} + ## https://developer.mozilla.org/en-US/docs/Web/API/Element/before + + proc append*(self: Node; element: Node): Node {.importjs: "#.$1(@)", varargs.} + ## https://developer.mozilla.org/en-US/docs/Web/API/Element/append + + proc closest*(self: Node; cssSelector: cstring): Node {.importjs: "#.$1(#)".} + ## https://developer.mozilla.org/en-US/docs/Web/API/Element/closest + + proc hasAttributeNS*(self: Node; namespace, localName: cstring): bool {.importjs: "(#.$1(#, #) || false)".} + ## https://developer.mozilla.org/en-US/docs/Web/API/Element/hasAttributeNS + + proc removeAttributeNS*(self: Node; namespace, attributeName: cstring) {.importjs: "#.$1(#, #)".} + ## https://developer.mozilla.org/en-US/docs/Web/API/Element/removeAttributeNS + + proc hasPointerCapture*(self: Node; pointerId: SomeNumber): bool {.importjs: "(#.$1(#) || false)".} + ## https://developer.mozilla.org/en-US/docs/Web/API/Element/hasPointerCapture + + proc releasePointerCapture*(self: Node; pointerId: SomeNumber) {.importjs: "#.$1(#)".} + ## https://developer.mozilla.org/en-US/docs/Web/API/Element/releasePointerCapture + + proc requestPointerLock*(self: Node) {.importjs: "#.$1()".} + ## https://developer.mozilla.org/en-US/docs/Web/API/Element/requestPointerLock + + proc replaceChildren*(self: Node; replacements: Node) {.importjs: "#.$1(@)", varargs.} + ## https://developer.mozilla.org/en-US/docs/Web/API/Element/replaceChildren + + proc replaceWith*(self: Node; replacements: Node) {.importjs: "#.$1(@)", varargs.} + ## https://developer.mozilla.org/en-US/docs/Web/API/Element/replaceWith + + proc scrollIntoViewIfNeeded*(self: Node; centerIfNeeded: bool) {.importjs: "#.$1(#)".} + ## https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollIntoViewIfNeeded + + proc setHTML*(self: Node; html: cstring) {.importjs: "#.$1(#)".} + ## https://developer.mozilla.org/en-US/docs/Web/API/Element/setHTML + + proc toggleAttribute*(self: Node; name: cstring; force = false): bool {.importjs: "(#.$1(#, #) || false)".} + ## https://developer.mozilla.org/en-US/docs/Web/API/Element/toggleAttribute + + proc matches*(self: Node; cssSelector: cstring): bool {.importjs: "(#.$1(#) || false)".} + ## https://developer.mozilla.org/en-US/docs/Web/API/Element/matches diff --git a/lib/js/jscore.nim b/lib/js/jscore.nim index 61c188431b85..781e8fd57ff0 100644 --- a/lib/js/jscore.nim +++ b/lib/js/jscore.nim @@ -123,3 +123,17 @@ since (1, 5): assert [1, 2, 3, 4, 5].copyWithin(0, 3) == @[4, 5, 3, 4, 5] assert [1, 2, 3, 4, 5].copyWithin(0, 3, 4) == @[4, 2, 3, 4, 5] assert [1, 2, 3, 4, 5].copyWithin(-2, -3, -1) == @[1, 2, 3, 3, 4] + + +since (1, 7): + func shift*[T](self: seq[T]): T {.importjs: "#.$1()".} = + ## https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/shift + runnableExamples: + var arrai = @[1, 2, 3] + assert arrai.shift() == 1 + assert arrai == @[2, 3] + + func queueMicrotask*(function: proc) {.importjs: "$1(#)".} = + ## * https://developer.mozilla.org/en-US/docs/Web/API/queueMicrotask + ## * https://developer.mozilla.org/en-US/docs/Web/API/HTML_DOM_API/Microtask_guide + runnableExamples"-r:off": queueMicrotask(proc() = echo "Microtask") diff --git a/lib/js/jsffi.nim b/lib/js/jsffi.nim index ac963eb899fb..aca4fc29214f 100644 --- a/lib/js/jsffi.nim +++ b/lib/js/jsffi.nim @@ -64,7 +64,7 @@ proc validJsName(name: string): bool = if chr notin {'A'..'Z','a'..'z','_','$','0'..'9'}: return false -template mangleJsName(name: cstring): cstring = +template mangleJsName(name: string): string = inc nameCounter "mangledName" & $nameCounter @@ -233,7 +233,7 @@ macro `.`*(obj: JsObject, field: untyped): JsObject = helper(`obj`) else: if not mangledNames.hasKey($field): - mangledNames[$field] = $mangleJsName($field) + mangledNames[$field] = mangleJsName($field) let importString = "#." & mangledNames[$field] result = quote do: proc helper(o: JsObject): JsObject @@ -251,7 +251,7 @@ macro `.=`*(obj: JsObject, field, value: untyped): untyped = helper(`obj`, `value`) else: if not mangledNames.hasKey($field): - mangledNames[$field] = $mangleJsName($field) + mangledNames[$field] = mangleJsName($field) let importString = "#." & mangledNames[$field] & " = #" result = quote do: proc helper(o: JsObject, v: auto) @@ -282,7 +282,7 @@ macro `.()`*(obj: JsObject, importString = "#." & $field & "(@)" else: if not mangledNames.hasKey($field): - mangledNames[$field] = $mangleJsName($field) + mangledNames[$field] = mangleJsName($field) importString = "#." & mangledNames[$field] & "(@)" result = quote: proc helper(o: JsObject): JsObject @@ -302,7 +302,7 @@ macro `.`*[K: cstring, V](obj: JsAssoc[K, V], importString = "#." & $field else: if not mangledNames.hasKey($field): - mangledNames[$field] = $mangleJsName($field) + mangledNames[$field] = mangleJsName($field) importString = "#." & mangledNames[$field] result = quote do: proc helper(o: type(`obj`)): `obj`.V @@ -319,7 +319,7 @@ macro `.=`*[K: cstring, V](obj: JsAssoc[K, V], importString = "#." & $field & " = #" else: if not mangledNames.hasKey($field): - mangledNames[$field] = $mangleJsName($field) + mangledNames[$field] = mangleJsName($field) importString = "#." & mangledNames[$field] & " = #" result = quote do: proc helper(o: type(`obj`), v: `obj`.V) @@ -348,8 +348,8 @@ iterator pairs*(obj: JsObject): (cstring, JsObject) = var k: cstring var v: JsObject {.emit: "for (var `k` in `obj`) {".} - {.emit: " if (!`obj`.hasOwnProperty(`k`)) continue;".} - {.emit: " `v`=`obj`[`k`];".} + {.emit: " if (!`obj`.hasOwnProperty(`k`)) { continue; }".} + {.emit: " `v` = `obj`[`k`];".} yield (k, v) {.emit: "}".} @@ -357,8 +357,8 @@ iterator items*(obj: JsObject): JsObject = ## Yields the `values` of each field in a JsObject, wrapped into a JsObject. var v: JsObject {.emit: "for (var k in `obj`) {".} - {.emit: " if (!`obj`.hasOwnProperty(k)) continue;".} - {.emit: " `v`=`obj`[k];".} + {.emit: " if (!`obj`.hasOwnProperty(k)) { continue; }".} + {.emit: " `v` = `obj`[k];".} yield v {.emit: "}".} @@ -366,7 +366,7 @@ iterator keys*(obj: JsObject): cstring = ## Yields the `names` of each field in a JsObject. var k: cstring {.emit: "for (var `k` in `obj`) {".} - {.emit: " if (!`obj`.hasOwnProperty(`k`)) continue;".} + {.emit: " if (!`obj`.hasOwnProperty(`k`)) { continue; }".} yield k {.emit: "}".} @@ -376,8 +376,8 @@ iterator pairs*[K: JsKey, V](assoc: JsAssoc[K, V]): (K,V) = var k: cstring var v: V {.emit: "for (var `k` in `assoc`) {".} - {.emit: " if (!`assoc`.hasOwnProperty(`k`)) continue;".} - {.emit: " `v`=`assoc`[`k`];".} + {.emit: " if (!`assoc`.hasOwnProperty(`k`)) { continue; }".} + {.emit: " `v` = `assoc`[`k`];".} yield (k.toJsKey(K), v) {.emit: "}".} @@ -385,8 +385,8 @@ iterator items*[K, V](assoc: JsAssoc[K, V]): V = ## Yields the `values` in a JsAssoc. var v: V {.emit: "for (var k in `assoc`) {".} - {.emit: " if (!`assoc`.hasOwnProperty(k)) continue;".} - {.emit: " `v`=`assoc`[k];".} + {.emit: " if (!`assoc`.hasOwnProperty(k)) { continue; }".} + {.emit: " `v` = `assoc`[k];".} yield v {.emit: "}".} @@ -394,7 +394,7 @@ iterator keys*[K: JsKey, V](assoc: JsAssoc[K, V]): K = ## Yields the `keys` in a JsAssoc. var k: cstring {.emit: "for (var `k` in `assoc`) {".} - {.emit: " if (!`assoc`.hasOwnProperty(`k`)) continue;".} + {.emit: " if (!`assoc`.hasOwnProperty(`k`)) { continue; }".} yield k.toJsKey(K) {.emit: "}".} diff --git a/lib/js/jsre.nim b/lib/js/jsre.nim index cd8fb2be72cc..19888aaa90c7 100644 --- a/lib/js/jsre.nim +++ b/lib/js/jsre.nim @@ -20,6 +20,7 @@ type RegExp* = ref object of JsRoot lastParen*: cstring ## Ditto. leftContext*: cstring ## Ditto. rightContext*: cstring ## Ditto. + hasIndices*: bool ## https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/hasIndices func newRegExp*(pattern: cstring; flags: cstring): RegExp {.importjs: "new RegExp(@)".} @@ -33,13 +34,13 @@ func compile*(self: RegExp; pattern: cstring; flags: cstring) {.importjs: "#.com func replace*(pattern: cstring; self: RegExp; replacement: cstring): cstring {.importjs: "#.replace(#, #)".} ## Returns a new string with some or all matches of a pattern replaced by given replacement -func split*(pattern: cstring; self: RegExp): seq[cstring] {.importjs: "#.split(#)".} +func split*(pattern: cstring; self: RegExp): seq[cstring] {.importjs: "(#.split(#) || [])".} ## Divides a string into an ordered list of substrings and returns the array -func match*(pattern: cstring; self: RegExp): seq[cstring] {.importjs: "#.match(#)".} +func match*(pattern: cstring; self: RegExp): seq[cstring] {.importjs: "(#.match(#) || [])".} ## Returns an array of matches of a RegExp against given string -func exec*(self: RegExp; pattern: cstring): seq[cstring] {.importjs: "#.exec(#)".} +func exec*(self: RegExp; pattern: cstring): seq[cstring] {.importjs: "(#.exec(#) || [])".} ## Executes a search for a match in its string parameter. func toCstring*(self: RegExp): cstring {.importjs: "#.toString()".} @@ -87,3 +88,5 @@ runnableExamples: assert "do1ne".split(jsregex) == @["do".cstring, "ne".cstring] jsregex.compile(r"[lw]", r"i") assert "hello world".replace(jsregex,"X") == "heXlo world" + let digitsRegex: RegExp = newRegExp(r"\d") + assert "foo".match(digitsRegex) == @[] diff --git a/lib/nimhcr.nim b/lib/nimhcr.nim index 2846f931b151..b35ecf8df833 100644 --- a/lib/nimhcr.nim +++ b/lib/nimhcr.nim @@ -487,7 +487,7 @@ when defined(createNimHcr): recursiveDiscovery(modules[curr].imports) allModulesOrderedByDFS.add(curr) continue - loadDll(curr) + loadDll(curr.cstring) # first load all dependencies of the current module and init it after that recursiveDiscovery(modules[curr].imports) @@ -497,20 +497,20 @@ when defined(createNimHcr): proc initModules() = # first init the pointers to hcr functions and also do the registering of typeinfo globals for curr in modulesToInit: - initHcrData(curr) - initTypeInfoGlobals(curr) + initHcrData(curr.cstring) + initTypeInfoGlobals(curr.cstring) # for now system always gets fully inited before any other module (including when reloading) - initPointerData(system) - initGlobalScope(system) + initPointerData(system.cstring) + initGlobalScope(system.cstring) # proceed with the DatInit calls - for all modules - including the main one! for curr in allModulesOrderedByDFS: if curr != system: - initPointerData(curr) + initPointerData(curr.cstring) mainDatInit() # execute top-level code (in global scope) for curr in modulesToInit: if curr != system: - initGlobalScope(curr) + initGlobalScope(curr.cstring) # cleanup old symbols which are gone now for curr in modulesToInit: cleanupSymbols(curr) diff --git a/lib/packages/docutils/dochelpers.nim b/lib/packages/docutils/dochelpers.nim index 8f97a7119542..b85e37983cbe 100644 --- a/lib/packages/docutils/dochelpers.nim +++ b/lib/packages/docutils/dochelpers.nim @@ -15,6 +15,10 @@ import rstast +when defined(nimPreviewSlimSystem): + import std/[assertions, syncio] + + type LangSymbol* = object ## symbol signature in Nim symKind*: string ## "proc", "const", "type", etc @@ -78,7 +82,7 @@ proc toLangSymbol*(linkText: PRstNode): LangSymbol = ## ## This proc should be kept in sync with the `renderTypes` proc from ## ``compiler/typesrenderer.nim``. - assert linkText.kind in {rnRef, rnInner} + assert linkText.kind in {rnRstRef, rnInner} const NimDefs = ["proc", "func", "macro", "method", "iterator", "template", "converter", "const", "type", "var", diff --git a/lib/packages/docutils/highlite.nim b/lib/packages/docutils/highlite.nim index d36b2c877e3c..4e62031eb125 100644 --- a/lib/packages/docutils/highlite.nim +++ b/lib/packages/docutils/highlite.nim @@ -59,6 +59,10 @@ import strutils from algorithm import binarySearch +when defined(nimPreviewSlimSystem): + import std/assertions + + type SourceLanguage* = enum langNone, langNim, langCpp, langCsharp, langC, langJava, @@ -125,9 +129,7 @@ proc initGeneralTokenizer*(g: var GeneralTokenizer, buf: cstring) = g.length = 0 g.state = low(TokenClass) g.lang = low(SourceLanguage) - var pos = 0 # skip initial whitespace: - while g.buf[pos] in {' ', '\t'..'\r'}: inc(pos) - g.pos = pos + g.pos = 0 proc initGeneralTokenizer*(g: var GeneralTokenizer, buf: string) = initGeneralTokenizer(g, cstring(buf)) diff --git a/lib/packages/docutils/rst.nim b/lib/packages/docutils/rst.nim index d4bfae48fc67..3c95c9ef0fff 100644 --- a/lib/packages/docutils/rst.nim +++ b/lib/packages/docutils/rst.nim @@ -7,224 +7,28 @@ # distribution, for details about the copyright. # -## ================================== -## rst -## ================================== +## This module implements a `reStructuredText`:idx: (RST) and +## `Markdown`:idx: parser. +## User's manual on supported markup syntax and command line usage can be +## found in [Nim-flavored Markdown and reStructuredText](markdown_rst.html). ## -## ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -## Nim-flavored reStructuredText and Markdown -## ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +## * See also [Nim DocGen Tools Guide](docgen.html) for handling of +## ``.nim`` files. +## * See also [packages/docutils/rstgen module](rstgen.html) to know how to +## generate HTML or Latex strings (for embedding them into custom documents). ## -## This module implements a `reStructuredText`:idx: (RST) parser. -## A large subset is implemented with some limitations_ and -## `Nim-specific features`_. -## A few `extra features`_ of the `Markdown`:idx: syntax are -## also supported. -## -## Nim can output the result to HTML [#html]_ or Latex [#latex]_. -## -## .. [#html] commands `nim doc`:cmd: for ``*.nim`` files and -## `nim rst2html`:cmd: for ``*.rst`` files -## -## .. [#latex] commands `nim doc2tex`:cmd: for ``*.nim`` and -## `nim rst2tex`:cmd: for ``*.rst``. -## -## If you are new to RST please consider reading the following: -## -## 1) a short `quick introduction`_ -## 2) an `RST reference`_: a comprehensive cheatsheet for RST -## 3) a more formal 50-page `RST specification`_. -## -## Features -## -------- -## -## Supported standard RST features: -## -## * body elements -## + sections -## + transitions -## + paragraphs -## + bullet lists using \+, \*, \- -## + enumerated lists using arabic numerals or alphabet -## characters: 1. ... 2. ... *or* a. ... b. ... *or* A. ... B. ... -## + footnotes (including manually numbered, auto-numbered, auto-numbered -## with label, and auto-symbol footnotes) and citations -## + definition lists -## + field lists -## + option lists -## + indented literal blocks -## + quoted literal blocks -## + line blocks -## + simple tables -## + directives (see official documentation in `RST directives list`_): -## - ``image``, ``figure`` for including images and videos -## - ``code`` -## - ``contents`` (table of contents), ``container``, ``raw`` -## - ``include`` -## - admonitions: "attention", "caution", "danger", "error", "hint", -## "important", "note", "tip", "warning", "admonition" -## - substitution definitions: `replace` and `image` -## + comments -## * inline markup -## + *emphasis*, **strong emphasis**, -## ``inline literals``, hyperlink references (including embedded URI), -## substitution references, standalone hyperlinks, -## internal links (inline and outline) -## + \`interpreted text\` with roles ``:literal:``, ``:strong:``, -## ``emphasis``, ``:sub:``/``:subscript:``, ``:sup:``/``:superscript:`` -## (see `RST roles list`_ for description). -## + inline internal targets -## -## .. _`Nim-specific features`: -## -## Additional Nim-specific features: -## -## * directives: ``code-block`` [cmp:Sphinx]_, ``title``, -## ``index`` [cmp:Sphinx]_ -## * predefined roles -## - ``:nim:`` (default), ``:c:`` (C programming language), -## ``:python:``, ``:yaml:``, ``:java:``, ``:cpp:`` (C++), ``:csharp`` (C#). -## That is every language that `highlite `_ supports. -## They turn on appropriate syntax highlighting in inline code. -## -## .. Note:: default role for Nim files is ``:nim:``, -## for ``*.rst`` it's currently ``:literal:``. -## -## - generic command line highlighting roles: -## - ``:cmd:`` for commands and common shells syntax -## - ``:console:`` the same for interactive sessions -## (commands should be prepended by ``$``) -## - ``:program:`` for executable names [cmp:Sphinx]_ -## (one can just use ``:cmd:`` on single word) -## - ``:option:`` for command line options [cmp:Sphinx]_ -## - ``:tok:``, a role for highlighting of programming language tokens -## * ***triple emphasis*** (bold and italic) using \*\*\* -## * ``:idx:`` role for \`interpreted text\` to include the link to this -## text into an index (example: `Nim index`_). -## * double slash `//` in option lists serves as a prefix for any option that -## starts from a word (without any leading symbols like `-`, `--`, `/`):: -## -## //compile compile the project -## //doc generate documentation -## -## Here the dummy `//` will disappear, while options `compile`:option: -## and `doc`:option: will be left in the final document. -## -## .. [cmp:Sphinx] similar but different from the directives of -## Python `Sphinx directives`_ and `Sphinx roles`_ extensions -## -## .. _`extra features`: -## -## Optional additional features, turned on by ``options: RstParseOption`` in -## `proc rstParse`_: -## -## * emoji / smiley symbols -## * Markdown tables -## * Markdown code blocks -## * Markdown links -## * Markdown headlines -## * Markdown block quotes -## * using ``1`` as auto-enumerator in enumerated lists like RST ``#`` -## (auto-enumerator ``1`` can not be used with ``#`` in the same list) -## -## .. Note:: By default Nim has ``roSupportMarkdown`` and -## ``roSupportRawDirective`` turned **on**. -## -## .. warning:: Using Nim-specific features can cause other RST implementations -## to fail on your document. -## -## Idiosyncrasies -## -------------- -## -## Currently we do **not** aim at 100% Markdown or RST compatibility in inline -## markup recognition rules because that would provide very little user value. -## This parser has 2 modes for inline markup: -## -## 1) Markdown-like mode which is enabled by `roPreferMarkdown` option -## (turned **on** by default). -## -## .. Note:: RST features like directives are still turned **on** -## -## 2) Compatibility mode which is RST rules. -## -## .. Note:: in both modes the parser interpretes text between single -## backticks (code) identically: -## backslash does not escape; the only exception: ``\`` folowed by ` -## does escape so that we can always input a single backtick ` in -## inline code. However that makes impossible to input code with -## ``\`` at the end in *single* backticks, one must use *double* -## backticks:: -## -## `\` -- WRONG -## ``\`` -- GOOD -## So single backticks can always be input: `\`` will turn to ` code -## -## .. Attention:: -## We don't support some obviously poor design choices of Markdown (or RST). -## -## - no support for the rule of 2 spaces causing a line break in Markdown -## (use RST "line blocks" syntax for making line breaks) -## -## - interpretation of Markdown block quotes is also slightly different, -## e.g. case -## -## :: -## -## >>> foo -## > bar -## >>baz -## -## is a single 3rd-level quote `foo bar baz` in original Markdown, while -## in Nim we naturally see it as 3rd-level quote `foo` + 1st level `bar` + -## 2nd level `baz`: -## -## >>> foo -## > bar -## >>baz -## -## Limitations -## ----------- -## -## * no Unicode support in character width calculations -## * body elements -## - no roman numerals in enumerated lists -## - no doctest blocks -## - no grid tables -## - some directives are missing (check official `RST directives list`_): -## ``parsed-literal``, ``sidebar``, ``topic``, ``math``, ``rubric``, -## ``epigraph``, ``highlights``, ``pull-quote``, ``compound``, -## ``table``, ``csv-table``, ``list-table``, ``section-numbering``, -## ``header``, ``footer``, ``meta``, ``class`` -## - no ``role`` directives and no custom interpreted text roles -## - some standard roles are not supported (check `RST roles list`_) -## - no generic admonition support -## * inline markup -## - no simple-inline-markup -## - no embedded aliases -## -## Usage -## ----- -## -## See `Nim DocGen Tools Guide `_ for the details about -## `nim doc`:cmd:, `nim rst2html`:cmd: and `nim rst2tex`:cmd: commands. -## -## See `packages/docutils/rstgen module `_ to know how to -## generate HTML or Latex strings to embed them into your documents. -## -## .. _quick introduction: https://docutils.sourceforge.io/docs/user/rst/quickstart.html -## .. _RST reference: https://docutils.sourceforge.io/docs/user/rst/quickref.html -## .. _RST specification: https://docutils.sourceforge.io/docs/ref/rst/restructuredtext.html -## .. _RST directives list: https://docutils.sourceforge.io/docs/ref/rst/directives.html -## .. _RST roles list: https://docutils.sourceforge.io/docs/ref/rst/roles.html -## .. _Nim index: https://nim-lang.org/docs/theindex.html -## .. _Sphinx directives: https://www.sphinx-doc.org/en/master/usage/restructuredtext/directives.html -## .. _Sphinx roles: https://www.sphinx-doc.org/en/master/usage/restructuredtext/roles.html +## Choice between Markdown and RST as well as optional additional features are +## turned on by passing ``options:`` [RstParseOptions] to [proc rstParse]. import os, strutils, rstast, dochelpers, std/enumutils, algorithm, lists, sequtils, std/private/miscdollars, tables, strscans from highlite import SourceLanguage, getSourceLanguage +when defined(nimPreviewSlimSystem): + import std/[assertions, syncio] + + type RstParseOption* = enum ## options for the RST parser roSupportSmilies, ## make the RST parser support smilies like ``:)`` @@ -253,8 +57,10 @@ type MsgKind* = enum ## the possible messages meCannotOpenFile = "cannot open '$1'", meExpected = "'$1' expected", + meMissingClosing = "$1", meGridTableNotImplemented = "grid table is not implemented", meMarkdownIllformedTable = "illformed delimiter row of a Markdown table", + meIllformedTable = "Illformed table: $1", meNewSectionExpected = "new section expected $1", meGeneralParseError = "general parse error", meInvalidDirective = "invalid directive: '$1'", @@ -275,7 +81,7 @@ type proc rstnodeToRefname*(n: PRstNode): string proc addNodes*(n: PRstNode): string -proc getFieldValue*(n: PRstNode, fieldname: string): string +proc getFieldValue*(n: PRstNode, fieldname: string): string {.gcsafe.} proc getArgument*(n: PRstNode): string # ----------------------------- scanner part -------------------------------- @@ -322,7 +128,10 @@ const ":geek:": "icon_e_geek", ":ugeek:": "icon_e_ugeek" } - SandboxDirAllowlist = ["image", "code", "code-block"] + SandboxDirAllowlist = [ + "image", "code", "code-block", "admonition", "attention", "caution", + "container", "contents", "danger", "default-role", "error", "figure", + "hint", "important", "index", "note", "role", "tip", "title", "warning"] type TokType = enum @@ -534,17 +343,17 @@ type footnoteAnchor = "footnote anchor", headlineAnchor = "implicitly-generated headline anchor" AnchorSubst = object - mainAnchor: ref string # A reference name that will be inserted directly - # into HTML/Latex. It's declared as `ref` because - # it can be shared between aliases. info: TLineInfo # where the anchor was defined priority: int case kind: range[arInternalRst .. arNim] of arInternalRst: anchorType: RstAnchorKind + target: PRstNode of arNim: tooltip: string # displayed tooltip for Nim-generated anchors langSym: LangSymbol + refname: string # A reference name that will be inserted directly + # into HTML/Latex. AnchorSubstTable = Table[string, seq[AnchorSubst]] # use `seq` to account for duplicate anchors FootnoteType = enum @@ -564,7 +373,7 @@ type filenameToIdx*: Table[string, FileIndex] idxToFilename*: seq[string] RstSharedState = object - options: RstParseOptions # parsing options + options*: RstParseOptions # parsing options hLevels: LevelMap # hierarchy of heading styles hTitleCnt: int # =0 if no title, =1 if only main title, # =2 if both title and subtitle are present @@ -588,9 +397,14 @@ type filenames*: RstFileTable # map file name <-> FileIndex (for storing # file names for warnings after 1st stage) currFileIdx*: FileIndex # current index in `filenames` + tocPart*: seq[PRstNode] # all the headings of a document hasToc*: bool PRstSharedState* = ref RstSharedState + ManualAnchor = object + alias: string # a (short) name that can substitute the `anchor` + anchor: string # anchor = id = refname + info: TLineInfo RstParser = object of RootObj idx*: int tok*: TokenSeq @@ -600,8 +414,9 @@ type ## documenation fragment that will be added ## in case of error/warning reporting to ## (relative) line/column of the token. - curAnchor*: string # variable to track latest anchor in s.anchors - curAnchorName*: string # corresponding name in human-readable format + curAnchors*: seq[ManualAnchor] + ## seq to accumulate aliases for anchors: + ## because RST can have >1 alias per 1 anchor EParseError* = object of ValueError @@ -683,14 +498,16 @@ proc currFilename(s: PRstSharedState): string = proc newRstSharedState*(options: RstParseOptions, filename: string, findFile: FindFileHandler, - msgHandler: MsgHandler): PRstSharedState = + msgHandler: MsgHandler, + hasToc: bool): PRstSharedState = let r = defaultRole(options) result = PRstSharedState( currRole: r, currRoleKind: whichRoleAux(r), options: options, msgHandler: if not isNil(msgHandler): msgHandler else: defaultMsgHandler, - findFile: if not isNil(findFile): findFile else: defaultFindFile + findFile: if not isNil(findFile): findFile else: defaultFindFile, + hasToc: hasToc ) setCurrFilename(result, filename) @@ -939,40 +756,28 @@ proc internalRefPriority(k: RstAnchorKind): int = of footnoteAnchor: result = 4 of headlineAnchor: result = 3 -proc addAnchorRst(p: var RstParser, name: string, refn: string, reset: bool, +proc addAnchorRst(p: var RstParser, name: string, target: PRstNode, anchorType: RstAnchorKind) = - ## Adds anchor `refn` with an alias `name` and - ## updates the corresponding `curAnchor` / `curAnchorName`. + ## Associates node `target` (which has field `anchor`) with an + ## alias `name` and updates the corresponding aliases in `p.curAnchors`. let prio = internalRefPriority(anchorType) - if p.curAnchorName == "": - var anchRef = new string - anchRef[] = refn + for a in p.curAnchors: + p.s.anchors.mgetOrPut(a.alias, newSeq[AnchorSubst]()).add( + AnchorSubst(kind: arInternalRst, target: target, priority: prio, + info: a.info, anchorType: manualDirectiveAnchor)) + if name != "": p.s.anchors.mgetOrPut(name, newSeq[AnchorSubst]()).add( - AnchorSubst(kind: arInternalRst, mainAnchor: anchRef, priority: prio, + AnchorSubst(kind: arInternalRst, target: target, priority: prio, info: prevLineInfo(p), anchorType: anchorType)) - else: - # override previous mainAnchor by `ref` in all aliases - var anchRef = p.s.anchors[p.curAnchorName][0].mainAnchor - anchRef[] = refn - p.s.anchors.mgetOrPut(name, newSeq[AnchorSubst]()).add( - AnchorSubst(kind: arInternalRst, mainAnchor: anchRef, priority: prio, - info: prevLineInfo(p), anchorType: anchorType)) - if reset: - p.curAnchor = "" - p.curAnchorName = "" - else: - p.curAnchor = refn - p.curAnchorName = name + p.curAnchors.setLen 0 proc addAnchorNim*(s: var PRstSharedState, refn: string, tooltip: string, langSym: LangSymbol, priority: int, info: TLineInfo) = - ## Adds an anchor `refn` (`mainAnchor`), which follows + ## Adds an anchor `refn`, which follows ## the rule `arNim` (i.e. a symbol in ``*.nim`` file) - var anchRef = new string - anchRef[] = refn s.anchors.mgetOrPut(langSym.name, newSeq[AnchorSubst]()).add( - AnchorSubst(kind: arNim, mainAnchor: anchRef, langSym: langSym, + AnchorSubst(kind: arNim, refname: refn, langSym: langSym, tooltip: tooltip, priority: priority, info: info)) @@ -1144,10 +949,9 @@ proc getAutoSymbol(s: PRstSharedState, order: int): string = proc newRstNodeA(p: var RstParser, kind: RstNodeKind): PRstNode = ## create node and consume the current anchor result = newRstNode(kind) - if p.curAnchor != "": - result.anchor = p.curAnchor - p.curAnchor = "" - p.curAnchorName = "" + if p.curAnchors.len > 0: + result.anchor = p.curAnchors[0].anchor + addAnchorRst(p, "", result, manualDirectiveAnchor) template newLeaf(s: string): PRstNode = newRstLeaf(s) @@ -1441,7 +1245,7 @@ proc parsePostfix(p: var RstParser, n: PRstNode): PRstNode = newSons = n.sons result = newRstNode(newKind, newSons) else: # some link that will be resolved in `resolveSubs` - newKind = rnRef + newKind = rnRstRef result = newRstNode(newKind, sons=newSons, info=n.info) elif match(p, p.idx, ":w:"): # a role: @@ -1535,7 +1339,7 @@ proc parseWordOrRef(p: var RstParser, father: PRstNode) = while currentTok(p).kind in {tkWord, tkPunct}: if currentTok(p).kind == tkPunct: if isInlineMarkupEnd(p, "_", exact=true): - reference = newRstNode(rnRef, info=lineInfo(p, saveIdx)) + reference = newRstNode(rnRstRef, info=lineInfo(p, saveIdx)) break if not validRefnamePunct(currentTok(p).symbol): break @@ -1615,39 +1419,123 @@ proc parseUntil(p: var RstParser, father: PRstNode, postfix: string, inc p.idx else: rstMessage(p, meExpected, postfix, line, col) +proc parseMarkdownCodeblockFields(p: var RstParser): PRstNode = + ## Parses additional (after language string) code block parameters + ## in a format *suggested* in the `CommonMark Spec`_ with handling of `"`. + if currentTok(p).kind == tkIndent: + result = nil + else: + result = newRstNode(rnFieldList) + while currentTok(p).kind != tkIndent: + if currentTok(p).kind == tkWhite: + inc p.idx + else: + let field = newRstNode(rnField) + var fieldName = "" + while currentTok(p).kind notin {tkWhite, tkIndent, tkEof} and + currentTok(p).symbol != "=": + fieldName.add currentTok(p).symbol + inc p.idx + field.add(newRstNode(rnFieldName, @[newLeaf(fieldName)])) + if currentTok(p).kind == tkWhite: inc p.idx + let fieldBody = newRstNode(rnFieldBody) + if currentTok(p).symbol == "=": + inc p.idx + if currentTok(p).kind == tkWhite: inc p.idx + var fieldValue = "" + if currentTok(p).symbol == "\"": + while true: + fieldValue.add currentTok(p).symbol + inc p.idx + if currentTok(p).kind == tkEof: + rstMessage(p, meExpected, "\"") + elif currentTok(p).symbol == "\"": + fieldValue.add "\"" + inc p.idx + break + else: + while currentTok(p).kind notin {tkWhite, tkIndent, tkEof}: + fieldValue.add currentTok(p).symbol + inc p.idx + fieldBody.add newLeaf(fieldValue) + field.add(fieldBody) + result.add(field) + +proc mayLoadFile(p: RstParser, result: var PRstNode) = + var filename = strip(getFieldValue(result, "file"), + chars = Whitespace + {'"'}) + if filename != "": + if roSandboxDisabled notin p.s.options: + let tok = p.tok[p.idx-2] + rstMessage(p, meSandboxedDirective, "file", tok.line, tok.col) + var path = p.findRelativeFile(filename) + if path == "": rstMessage(p, meCannotOpenFile, filename) + var n = newRstNode(rnLiteralBlock) + n.add newLeaf(readFile(path)) + result.sons[2] = n + +proc defaultCodeLangNim(p: RstParser, result: var PRstNode) = + # Create a field block if the input block didn't have any. + if result.sons[1].isNil: result.sons[1] = newRstNode(rnFieldList) + assert result.sons[1].kind == rnFieldList + # Hook the extra field and specify the Nim language as value. + var extraNode = newRstNode(rnField, info=lineInfo(p)) + extraNode.add(newRstNode(rnFieldName)) + extraNode.add(newRstNode(rnFieldBody)) + extraNode.sons[0].add newLeaf("default-language") + extraNode.sons[1].add newLeaf("Nim") + result.sons[1].add(extraNode) + proc parseMarkdownCodeblock(p: var RstParser): PRstNode = result = newRstNodeA(p, rnCodeBlock) + result.sons.setLen(3) + let line = curLine(p) + let baseCol = currentTok(p).col + let baseSym = currentTok(p).symbol # usually just ``` + inc p.idx result.info = lineInfo(p) var args = newRstNode(rnDirArg) if currentTok(p).kind == tkWord: args.add(newLeaf(p)) inc p.idx + result.sons[1] = parseMarkdownCodeblockFields(p) + mayLoadFile(p, result) else: args = nil var n = newLeaf("") while true: - case currentTok(p).kind - of tkEof: - rstMessage(p, meExpected, "```") + if currentTok(p).kind == tkEof: + rstMessage(p, meMissingClosing, + "$1 (started at line $2)" % [baseSym, $line]) break - of tkPunct, tkAdornment: - if currentTok(p).symbol == "```": - inc p.idx - break - else: - n.text.add(currentTok(p).symbol) - inc p.idx + elif nextTok(p).kind in {tkPunct, tkAdornment} and + nextTok(p).symbol[0] == baseSym[0] and + nextTok(p).symbol.len >= baseSym.len: + inc p.idx, 2 + break + elif currentTok(p).kind == tkIndent: + n.text.add "\n" + if currentTok(p).ival > baseCol: + n.text.add " ".repeat(currentTok(p).ival - baseCol) + elif currentTok(p).ival < baseCol: + rstMessage(p, mwRstStyle, + "unexpected de-indentation in Markdown code block") + inc p.idx else: n.text.add(currentTok(p).symbol) inc p.idx - var lb = newRstNode(rnLiteralBlock) - lb.add(n) - result.add(args) - result.add(PRstNode(nil)) - result.add(lb) + result.sons[0] = args + if result.sons[2] == nil: + var lb = newRstNode(rnLiteralBlock) + lb.add(n) + result.sons[2] = lb + if result.sons[0].isNil and roNimFile in p.s.options: + defaultCodeLangNim(p, result) proc parseMarkdownLink(p: var RstParser; father: PRstNode): bool = - var desc, link = "" + # Parses Markdown link. If it's Pandoc auto-link then its second + # son (target) will be in tokenized format (rnInner with leafs). + var desc = newRstNode(rnInner) var i = p.idx var parensStack: seq[char] @@ -1655,31 +1543,59 @@ proc parseMarkdownLink(p: var RstParser; father: PRstNode): bool = parensStack.setLen 0 inc i # skip begin token while true: - if p.tok[i].kind in {tkEof, tkIndent}: return false + if p.tok[i].kind == tkEof: return false + if p.tok[i].kind == tkIndent and p.tok[i+1].kind == tkIndent: + return false let isClosing = checkParen(p.tok[i], parensStack) if p.tok[i].symbol == endToken and not isClosing: break - dest.add p.tok[i].symbol + let symbol = if p.tok[i].kind == tkIndent: " " else: p.tok[i].symbol + when dest is string: dest.add symbol + else: dest.add newLeaf(symbol) inc i inc i # skip end token parse("]", desc) - if p.tok[i].symbol != "(": return false - let linkIdx = i + 1 - parse(")", link) - # only commit if we detected no syntax error: - let protocol = safeProtocol(link) - if link == "": - result = false - rstMessage(p, mwBrokenLink, protocol, - p.tok[linkIdx].line, p.tok[linkIdx].col) - else: - let child = newRstNode(rnHyperlink) - child.add desc - child.add link - father.add child + if p.tok[i].symbol == "(": + var link = "" + let linkIdx = i + 1 + parse(")", link) + # only commit if we detected no syntax error: + let protocol = safeProtocol(link) + if link == "": + result = false + rstMessage(p, mwBrokenLink, protocol, + p.tok[linkIdx].line, p.tok[linkIdx].col) + else: + let child = newRstNode(rnHyperlink) + child.add newLeaf(desc.addNodes) + child.add link + father.add child + p.idx = i + result = true + elif roPreferMarkdown in p.s.options: + # Use Pandoc's implicit_header_references extension + var n = newRstNode(rnPandocRef) + if p.tok[i].symbol == "[": + var link = newRstNode(rnInner) + let targetIdx = i + 1 + parse("]", link) + n.add desc + if link.len != 0: # [description][target] + n.add link + n.info = lineInfo(p, targetIdx) + else: # [description=target][] + n.add desc + n.info = lineInfo(p, p.idx + 1) + else: # [description=target] + n.add desc + n.add desc # target is the same as description + n.info = lineInfo(p, p.idx + 1) + father.add n p.idx = i result = true + else: + result = false proc getFootnoteType(label: PRstNode): (FootnoteType, int) = if label.sons.len >= 1 and label.sons[0].kind == rnLeaf and @@ -1729,6 +1645,19 @@ proc parseFootnoteName(p: var RstParser, reference: bool): PRstNode = inc i p.idx = i +proc isMarkdownCodeBlock(p: RstParser, idx: int): bool = + let tok = p.tok[idx] + template allowedSymbol: bool = + (tok.symbol[0] == '`' or + roPreferMarkdown in p.s.options and tok.symbol[0] == '~') + result = (roSupportMarkdown in p.s.options and + tok.kind in {tkPunct, tkAdornment} and + allowedSymbol and + tok.symbol.len >= 3) + +proc isMarkdownCodeBlock(p: RstParser): bool = + isMarkdownCodeBlock(p, p.idx) + proc parseInline(p: var RstParser, father: PRstNode) = var n: PRstNode # to be used in `if` condition let saveIdx = p.idx @@ -1750,12 +1679,11 @@ proc parseInline(p: var RstParser, father: PRstNode) = var n = newRstNode(rnInlineTarget) inc p.idx parseUntil(p, n, "`", false) - let refn = rstnodeToRefname(n) - addAnchorRst(p, name = linkName(n), refn = refn, reset = true, + n.anchor = rstnodeToRefname(n) + addAnchorRst(p, name = linkName(n), target = n, anchorType=manualInlineAnchor) father.add(n) - elif roSupportMarkdown in p.s.options and currentTok(p).symbol == "```": - inc p.idx + elif isMarkdownCodeBlock(p): father.add(parseMarkdownCodeblock(p)) elif isInlineMarkupStart(p, "``"): var n = newRstNode(rnInlineLiteral) @@ -1815,8 +1743,7 @@ proc parseInline(p: var RstParser, father: PRstNode) = return parseWordOrRef(p, father) of tkAdornment, tkOther, tkWhite: - if roSupportMarkdown in p.s.options and currentTok(p).symbol == "```": - inc p.idx + if isMarkdownCodeBlock(p): father.add(parseMarkdownCodeblock(p)) return if roSupportSmilies in p.s.options: @@ -1924,7 +1851,22 @@ proc getWrappableIndent(p: RstParser): int = elif nextIndent >= currentTok(p).col: # may be a definition list [case.2] result = currentTok(p).col else: - result = nextIndent # [case.3] + result = nextIndent # allow parsing next lines [case.3] + +proc getMdBlockIndent(p: RstParser): int = + ## Markdown version of `getWrappableIndent`. + if currentTok(p).kind == tkIndent: + result = currentTok(p).ival + else: + var nextIndent = p.tok[tokenAfterNewline(p)-1].ival + # TODO: Markdown-compliant definition should allow nextIndent == currInd(p): + if nextIndent <= currInd(p): # parse only this line + result = currentTok(p).col + else: + result = nextIndent # allow parsing next lines [case.3] + +template isRst(p: RstParser): bool = roPreferMarkdown notin p.s.options +template isMd(p: RstParser): bool = roPreferMarkdown in p.s.options proc parseField(p: var RstParser): PRstNode = ## Returns a parsed rnField node. @@ -2089,6 +2031,8 @@ proc isAdornmentHeadline(p: RstParser, adornmentIdx: int): bool = ## No support for Unicode. if p.tok[adornmentIdx].symbol in ["::", "..", "|"]: return false + if isMarkdownCodeBlock(p, adornmentIdx): + return false var headlineLen = 0 var failure = "" if p.idx < adornmentIdx: # check for underline @@ -2167,6 +2111,39 @@ proc isDefList(p: RstParser): bool = p.tok[j].kind in {tkWord, tkOther, tkPunct} and p.tok[j - 2].symbol != "::" +proc `$`(t: Token): string = # for debugging only + result = "(" & $t.kind & " line=" & $t.line & " col=" & $t.col + if t.kind == tkIndent: result = result & " ival=" & $t.ival & ")" + else: result = result & " symbol=" & t.symbol & ")" + +proc skipNewlines(p: RstParser, j: int): int = + result = j + while p.tok[result].kind != tkEof and p.tok[result].kind == tkIndent: + inc result # skip blank lines + +proc skipNewlines(p: var RstParser) = + p.idx = skipNewlines(p, p.idx) + +const maxMdRelInd = 3 ## In Markdown: maximum indentation that does not yet + ## make the indented block a code + +proc isMdRelInd(outerInd, nestedInd: int): bool = + result = outerInd <= nestedInd and nestedInd <= outerInd + maxMdRelInd + +proc isMdDefBody(p: RstParser, j: int, termCol: int): bool = + let defCol = p.tok[j].col + result = p.tok[j].symbol == ":" and + isMdRelInd(termCol, defCol) and + p.tok[j+1].kind == tkWhite and + p.tok[j+2].kind in {tkWord, tkOther, tkPunct} + +proc isMdDefListItem(p: RstParser, idx: int): bool = + var j = tokenAfterNewline(p, idx) + j = skipNewlines(p, j) + let termCol = p.tok[j].col + result = isMdRelInd(currInd(p), termCol) and + isMdDefBody(p, j, termCol) + proc isOptionList(p: RstParser): bool = result = match(p, p.idx, "-w") or match(p, p.idx, "--w") or match(p, p.idx, "/w") or match(p, p.idx, "//w") @@ -2193,7 +2170,7 @@ proc findPipe(p: RstParser, start: int): bool = proc whichSection(p: RstParser): RstNodeKind = if currentTok(p).kind in {tkAdornment, tkPunct}: # for punctuation sequences that can be both tkAdornment and tkPunct - if roSupportMarkdown in p.s.options and currentTok(p).symbol == "```": + if isMarkdownCodeBlock(p): return rnCodeBlock elif currentTok(p).symbol == "::": return rnLiteralBlock @@ -2239,8 +2216,10 @@ proc whichSection(p: RstParser): RstNodeKind = result = rnEnumList elif isOptionList(p): result = rnOptionList - elif isDefList(p): + elif isRst(p) and isDefList(p): result = rnDefList + elif isMd(p) and isMdDefListItem(p, p.idx): + result = rnMdDefList else: result = rnParagraph of tkWord, tkOther, tkWhite: @@ -2249,7 +2228,9 @@ proc whichSection(p: RstParser): RstNodeKind = if isAdornmentHeadline(p, tokIdx): result = rnHeadline else: result = rnParagraph elif match(p, p.idx, "e) ") or match(p, p.idx, "e. "): result = rnEnumList - elif isDefList(p): result = rnDefList + elif isRst(p) and isDefList(p): result = rnDefList + elif isMd(p) and isMdDefListItem(p, p.idx): + result = rnMdDefList else: result = rnParagraph else: result = rnLeaf @@ -2440,8 +2421,8 @@ proc parseHeadline(p: var RstParser): PRstNode = result.level = getLevel(p, c, hasOverline=false) checkHeadingHierarchy(p, result.level) p.s.hCurLevel = result.level - addAnchorRst(p, linkName(result), rstnodeToRefname(result), reset=true, - anchorType=headlineAnchor) + addAnchorRst(p, linkName(result), result, anchorType=headlineAnchor) + p.s.tocPart.add result proc parseOverline(p: var RstParser): PRstNode = var c = currentTok(p).symbol[0] @@ -2463,85 +2444,207 @@ proc parseOverline(p: var RstParser): PRstNode = if currentTok(p).kind == tkAdornment: inc p.idx if currentTok(p).kind == tkIndent: inc p.idx - addAnchorRst(p, linkName(result), rstnodeToRefname(result), reset=true, - anchorType=headlineAnchor) + addAnchorRst(p, linkName(result), result, anchorType=headlineAnchor) + p.s.tocPart.add result + +proc fixHeadlines(s: PRstSharedState) = + # Fix up section levels depending on presence of a title and subtitle: + for n in s.tocPart: + if n.kind in {rnHeadline, rnOverline}: + if s.hTitleCnt == 2: + if n.level == 1: # it's the subtitle + n.level = 0 + elif n.level >= 2: # normal sections, start numbering from 1 + n.level -= 1 + elif s.hTitleCnt == 0: + n.level += 1 + # Set headline anchors: + for iHeading in 0 .. s.tocPart.high: + let n: PRstNode = s.tocPart[iHeading] + if n.level >= 1: + n.anchor = rstnodeToRefname(n) + # Fix anchors for uniqueness if `.. contents::` is present + if s.hasToc: + # Find the last higher level section for unique reference name + var sectionPrefix = "" + for i in countdown(iHeading - 1, 0): + if s.tocPart[i].level >= 1 and s.tocPart[i].level < n.level: + sectionPrefix = rstnodeToRefname(s.tocPart[i]) & "-" + break + if sectionPrefix != "": + n.anchor = sectionPrefix & n.anchor + s.tocPart.setLen 0 type - IntSeq = seq[int] - ColumnLimits = tuple + ColSpec = object + start, stop: int + RstCols = seq[ColSpec] + ColumnLimits = tuple # for Markdown first, last: int ColSeq = seq[ColumnLimits] +proc tokStart(p: RstParser, idx: int): int = + result = p.tok[idx].col + +proc tokStart(p: RstParser): int = + result = tokStart(p, p.idx) + +proc tokEnd(p: RstParser, idx: int): int = + result = p.tok[idx].col + p.tok[idx].symbol.len - 1 + proc tokEnd(p: RstParser): int = - result = currentTok(p).col + currentTok(p).symbol.len - 1 + result = tokEnd(p, p.idx) -proc getColumns(p: var RstParser, cols: var IntSeq) = +proc getColumns(p: RstParser, cols: var RstCols, startIdx: int): int = + # Fills table column specification (or separator) `cols` and returns + # the next parser index after it. var L = 0 + result = startIdx while true: inc L setLen(cols, L) - cols[L - 1] = tokEnd(p) - assert(currentTok(p).kind == tkAdornment) - inc p.idx - if currentTok(p).kind != tkWhite: break - inc p.idx - if currentTok(p).kind != tkAdornment: break - if currentTok(p).kind == tkIndent: inc p.idx - # last column has no limit: - cols[L - 1] = 32000 + cols[L - 1].start = tokStart(p, result) + cols[L - 1].stop = tokEnd(p, result) + assert(p.tok[result].kind == tkAdornment) + inc result + if p.tok[result].kind != tkWhite: break + inc result + if p.tok[result].kind != tkAdornment: break + if p.tok[result].kind == tkIndent: inc result + +proc checkColumns(p: RstParser, cols: RstCols) = + var i = p.idx + if p.tok[i].symbol[0] != '=': + rstMessage(p, mwRstStyle, + "only tables with `=` columns specification are allowed") + for col in 0 ..< cols.len: + if tokEnd(p, i) != cols[col].stop: + rstMessage(p, meIllformedTable, + "end of table column #$1 should end at position $2" % [ + $(col+1), $(cols[col].stop+ColRstOffset)], + p.tok[i].line, tokEnd(p, i)) + inc i + if col == cols.len - 1: + if p.tok[i].kind == tkWhite: + inc i + if p.tok[i].kind notin {tkIndent, tkEof}: + rstMessage(p, meIllformedTable, "extraneous column specification") + elif p.tok[i].kind == tkWhite: + inc i + else: + rstMessage(p, meIllformedTable, "no enough table columns", + p.tok[i].line, p.tok[i].col) + +proc getSpans(p: RstParser, nextLine: int, + cols: RstCols, unitedCols: RstCols): seq[int] = + ## Calculates how many columns a joined cell occupies. + if unitedCols.len > 0: + result = newSeq[int](unitedCols.len) + var + iCell = 0 + jCell = 0 + uCell = 0 + while jCell < cols.len: + if cols[jCell].stop < unitedCols[uCell].stop: + inc jCell + elif cols[jCell].stop == unitedCols[uCell].stop: + result[uCell] = jCell - iCell + 1 + iCell = jCell + 1 + jCell = jCell + 1 + inc uCell + else: + rstMessage(p, meIllformedTable, + "spanning underline does not match main table columns", + p.tok[nextLine].line, p.tok[nextLine].col) + +proc parseSimpleTableRow(p: var RstParser, cols: RstCols, colChar: char): PRstNode = + ## Parses 1 row in RST simple table. + # Consider that columns may be spanning (united by using underline like ----): + let nextLine = tokenAfterNewline(p) + var unitedCols: RstCols + var afterSpan: int + if p.tok[nextLine].kind == tkAdornment and p.tok[nextLine].symbol[0] == '-': + afterSpan = getColumns(p, unitedCols, nextLine) + if unitedCols == cols and p.tok[nextLine].symbol[0] == colChar: + # legacy rst.nim compat.: allow punctuation like `----` in main boundaries + afterSpan = nextLine + unitedCols.setLen 0 + else: + afterSpan = nextLine + template colEnd(i): int = + if i == cols.len - 1: high(int) # last column has no limit + elif unitedCols.len > 0: unitedCols[i].stop else: cols[i].stop + template colStart(i): int = + if unitedCols.len > 0: unitedCols[i].start else: cols[i].start + var row = newSeq[string](if unitedCols.len > 0: unitedCols.len else: cols.len) + var spans: seq[int] = getSpans(p, nextLine, cols, unitedCols) + + let line = currentTok(p).line + # Iterate over the lines a single cell may span: + while true: + var nCell = 0 + # distribute tokens between cells in the current line: + while currentTok(p).kind notin {tkIndent, tkEof}: + if tokEnd(p) <= colEnd(nCell): + if tokStart(p) < colStart(nCell): + if currentTok(p).kind != tkWhite: + rstMessage(p, meIllformedTable, + "this word crosses table column from the left") + else: + inc p.idx + else: + row[nCell].add(currentTok(p).symbol) + inc p.idx + else: + if tokStart(p) < colEnd(nCell) and currentTok(p).kind != tkWhite: + rstMessage(p, meIllformedTable, + "this word crosses table column from the right") + inc nCell + if currentTok(p).kind == tkIndent: inc p.idx + if tokEnd(p) <= colEnd(0): break + # Continued current cells because the 1st column is empty. + if currentTok(p).kind in {tkEof, tkAdornment}: + break + for nCell in countup(1, high(row)): row[nCell].add('\n') + result = newRstNode(rnTableRow) + var q: RstParser + for uCell in 0 ..< row.len: + initParser(q, p.s) + q.col = colStart(uCell) + q.line = line - 1 + getTokens(row[uCell], q.tok) + let cell = newRstNode(rnTableDataCell) + cell.span = if spans.len == 0: 0 else: spans[uCell] + cell.add(parseDoc(q)) + result.add(cell) + if afterSpan > p.idx: + p.idx = afterSpan proc parseSimpleTable(p: var RstParser): PRstNode = - var - cols: IntSeq - row: seq[string] - i, last, line: int - c: char - q: RstParser - a, b: PRstNode + var cols: RstCols result = newRstNodeA(p, rnTable) - cols = @[] - row = @[] - a = nil - c = currentTok(p).symbol[0] + let startIdx = getColumns(p, cols, p.idx) + let colChar = currentTok(p).symbol[0] + checkColumns(p, cols) + p.idx = startIdx + result.colCount = cols.len while true: if currentTok(p).kind == tkAdornment: - last = tokenAfterNewline(p) - if p.tok[last].kind in {tkEof, tkIndent}: + checkColumns(p, cols) + p.idx = tokenAfterNewline(p) + if currentTok(p).kind in {tkEof, tkIndent}: # skip last adornment line: - p.idx = last break - getColumns(p, cols) - setLen(row, cols.len) - if a != nil: - for j in 0 ..< a.len: # fix rnTableDataCell -> rnTableHeaderCell - a.sons[j] = newRstNode(rnTableHeaderCell, a.sons[j].sons) + if result.sons.len > 0: result.sons[^1].endsHeader = true + # fix rnTableDataCell -> rnTableHeaderCell for previous table rows: + for nRow in 0 ..< result.sons.len: + for nCell in 0 ..< result.sons[nRow].len: + template cell: PRstNode = result.sons[nRow].sons[nCell] + cell = PRstNode(kind: rnTableHeaderCell, sons: cell.sons, + span: cell.span, anchor: cell.anchor) if currentTok(p).kind == tkEof: break - for j in countup(0, high(row)): row[j] = "" - # the following while loop iterates over the lines a single cell may span: - line = currentTok(p).line - while true: - i = 0 - while currentTok(p).kind notin {tkIndent, tkEof}: - if tokEnd(p) <= cols[i]: - row[i].add(currentTok(p).symbol) - inc p.idx - else: - if currentTok(p).kind == tkWhite: inc p.idx - inc i - if currentTok(p).kind == tkIndent: inc p.idx - if tokEnd(p) <= cols[0]: break - if currentTok(p).kind in {tkEof, tkAdornment}: break - for j in countup(1, high(row)): row[j].add('\n') - a = newRstNode(rnTableRow) - for j in countup(0, high(row)): - initParser(q, p.s) - q.col = cols[j] - q.line = line - 1 - getTokens(row[j], q.tok) - b = newRstNode(rnTableDataCell) - b.add(parseDoc(q)) - a.add(b) - result.add(a) + let tabRow = parseSimpleTableRow(p, cols, colChar) + result.add tabRow proc readTableRow(p: var RstParser): ColSeq = if currentTok(p).symbol == "|": inc p.idx @@ -2574,17 +2677,16 @@ proc isValidDelimiterRow(p: var RstParser, colNum: int): bool = proc parseMarkdownTable(p: var RstParser): PRstNode = var row: ColSeq - colNum: int a, b: PRstNode q: RstParser result = newRstNodeA(p, rnMarkdownTable) proc parseRow(p: var RstParser, cellKind: RstNodeKind, result: PRstNode) = row = readTableRow(p) - if colNum == 0: colNum = row.len # table header - elif row.len < colNum: row.setLen(colNum) + if result.colCount == 0: result.colCount = row.len # table header + elif row.len < result.colCount: row.setLen(result.colCount) a = newRstNode(rnTableRow) - for j in 0 ..< colNum: + for j in 0 ..< result.colCount: b = newRstNode(cellKind) initParser(q, p.s) q.col = p.col @@ -2595,7 +2697,8 @@ proc parseMarkdownTable(p: var RstParser): PRstNode = result.add(a) parseRow(p, rnTableHeaderCell, result) - if not isValidDelimiterRow(p, colNum): rstMessage(p, meMarkdownIllformedTable) + if not isValidDelimiterRow(p, result.colCount): + rstMessage(p, meMarkdownIllformedTable) while predNL(p) and currentTok(p).symbol == "|": parseRow(p, rnTableDataCell, result) @@ -2657,6 +2760,36 @@ proc parseOptionList(p: var RstParser): PRstNode = if currentTok(p).kind != tkEof: dec p.idx # back to tkIndent break +proc parseMdDefinitionList(p: var RstParser): PRstNode = + ## Parses (Pandoc/kramdown/PHPextra) Mardkown definition lists. + result = newRstNodeA(p, rnMdDefList) + let termCol = currentTok(p).col + while true: + var item = newRstNode(rnDefItem) + var term = newRstNode(rnDefName) + parseLine(p, term) + skipNewlines(p) + inc p.idx, 2 # skip ":" and space + item.add(term) + while true: + var def = newRstNode(rnDefBody) + let indent = getMdBlockIndent(p) + pushInd(p, indent) + parseSection(p, def) + popInd(p) + item.add(def) + let j = skipNewlines(p, p.idx) + if isMdDefBody(p, j, termCol): # parse next definition body + p.idx = j + 2 # skip ":" and space + else: + break + result.add(item) + let j = skipNewlines(p, p.idx) + if p.tok[j].col == termCol and isMdDefListItem(p, j): + p.idx = j # parse next item + else: + break + proc parseDefinitionList(p: var RstParser): PRstNode = result = nil var j = tokenAfterNewline(p) - 1 @@ -2797,11 +2930,19 @@ proc parseSection(p: var RstParser, result: PRstNode) = if currInd(p) == currentTok(p).ival: inc p.idx elif currentTok(p).ival > currInd(p): - pushInd(p, currentTok(p).ival) - var a = newRstNodeA(p, rnBlockQuote) - parseSection(p, a) - result.add(a) - popInd(p) + if roPreferMarkdown in p.s.options: # Markdown => normal paragraphs + if currentTok(p).ival - currInd(p) >= 4: + rstMessage(p, mwRstStyle, + "Markdown indented code not implemented") + pushInd(p, currentTok(p).ival) + parseSection(p, result) + popInd(p) + else: # RST mode => block quotes + pushInd(p, currentTok(p).ival) + var a = newRstNodeA(p, rnBlockQuote) + parseSection(p, a) + result.add(a) + popInd(p) else: while currentTok(p).kind != tkEof and nextTok(p).kind == tkIndent: inc p.idx # skip blank lines @@ -2822,6 +2963,7 @@ proc parseSection(p: var RstParser, result: PRstNode) = of rnLeaf: rstMessage(p, meNewSectionExpected, "(syntax error)") of rnParagraph: discard of rnDefList: a = parseDefinitionList(p) + of rnMdDefList: a = parseMdDefinitionList(p) of rnFieldList: if p.idx > 0: dec p.idx a = parseFields(p) @@ -2848,9 +2990,6 @@ proc parseSectionWrapper(p: var RstParser): PRstNode = while result.kind == rnInner and result.len == 1: result = result.sons[0] -proc `$`(t: Token): string = - result = $t.kind & ' ' & t.symbol - proc parseDoc(p: var RstParser): PRstNode = result = parseSectionWrapper(p) if currentTok(p).kind != tkEof: @@ -3028,29 +3167,11 @@ proc dirCodeBlock(p: var RstParser, nimExtension = false): PRstNode = ## file. This behaviour is disabled in sandboxed mode and can be re-enabled ## with the `roSandboxDisabled` flag. result = parseDirective(p, rnCodeBlock, {hasArg, hasOptions}, parseLiteralBlock) - var filename = strip(getFieldValue(result, "file")) - if filename != "": - if roSandboxDisabled notin p.s.options: - let tok = p.tok[p.idx-2] - rstMessage(p, meSandboxedDirective, "file", tok.line, tok.col) - var path = p.findRelativeFile(filename) - if path == "": rstMessage(p, meCannotOpenFile, filename) - var n = newRstNode(rnLiteralBlock) - n.add newLeaf(readFile(path)) - result.sons[2] = n + mayLoadFile(p, result) # Extend the field block if we are using our custom Nim extension. if nimExtension: - # Create a field block if the input block didn't have any. - if result.sons[1].isNil: result.sons[1] = newRstNode(rnFieldList) - assert result.sons[1].kind == rnFieldList - # Hook the extra field and specify the Nim language as value. - var extraNode = newRstNode(rnField, info=lineInfo(p)) - extraNode.add(newRstNode(rnFieldName)) - extraNode.add(newRstNode(rnFieldBody)) - extraNode.sons[0].add newLeaf("default-language") - extraNode.sons[1].add newLeaf("Nim") - result.sons[1].add(extraNode) + defaultCodeLangNim(p, result) proc dirContainer(p: var RstParser): PRstNode = result = parseDirective(p, rnContainer, {hasArg}, parseSectionWrapper) @@ -3068,6 +3189,7 @@ proc dirTitle(p: var RstParser): PRstNode = proc dirContents(p: var RstParser): PRstNode = result = parseDirective(p, rnContents, {hasArg}, nil) + p.s.hasToc = true proc dirIndex(p: var RstParser): PRstNode = result = parseDirective(p, rnIndex, {}, parseSectionWrapper) @@ -3202,7 +3324,7 @@ proc parseFootnote(p: var RstParser): PRstNode {.gcsafe.} = anchor.add $p.s.lineFootnoteSym.len of fnCitation: anchor.add rstnodeToRefname(label) - addAnchorRst(p, anchor, anchor, reset=true, anchorType=footnoteAnchor) + addAnchorRst(p, anchor, target = result, anchorType = footnoteAnchor) result.anchor = anchor if currentTok(p).kind == tkWhite: inc p.idx discard parseBlockContent(p, result, parseSectionWrapper) @@ -3236,8 +3358,9 @@ proc parseDotDot(p: var RstParser): PRstNode = if currentTok(p).kind == tkWhite: inc p.idx var b = untilEol(p) if len(b) == 0: # set internal anchor - addAnchorRst(p, linkName(a), rstnodeToRefname(a), reset=false, - anchorType=manualDirectiveAnchor) + p.curAnchors.add ManualAnchor( + alias: linkName(a), anchor: rstnodeToRefname(a), info: prevLineInfo(p) + ) else: # external hyperlink setRef(p, rstnodeToRefname(a), b, refType=hyperlinkAlias) elif match(p, p.idx, " |"): @@ -3278,11 +3401,19 @@ proc rstParsePass1*(fragment: string, proc preparePass2*(s: PRstSharedState, mainNode: PRstNode) = ## Records titles in node `mainNode` and orders footnotes. countTitles(s, mainNode) + fixHeadlines(s) orderFootnotes(s) proc resolveLink(s: PRstSharedState, n: PRstNode) : PRstNode = # Associate this link alias with its target and change node kind to # rnHyperlink or rnInternalRef appropriately. + var desc, alias: PRstNode + if n.kind == rnPandocRef: # link like [desc][alias] + desc = n.sons[0] + alias = n.sons[1] + else: # n.kind == rnRstRef, link like `desc=alias`_ + desc = n + alias = n type LinkDef = object ar: AnchorRule priority: int @@ -3294,33 +3425,33 @@ proc resolveLink(s: PRstSharedState, n: PRstNode) : PRstNode = if result == 0: result = cmp(x.target, y.target) var foundLinks: seq[LinkDef] - let text = newRstNode(rnInner, n.sons) - let refn = rstnodeToRefname(n) + let refn = rstnodeToRefname(alias) var hyperlinks = findRef(s, refn) for y in hyperlinks: foundLinks.add LinkDef(ar: arHyperlink, priority: refPriority(y.kind), target: y.value, info: y.info, tooltip: "(" & $y.kind & ")") - let substRst = findMainAnchorRst(s, text.addNodes, n.info) + let substRst = findMainAnchorRst(s, alias.addNodes, n.info) for subst in substRst: foundLinks.add LinkDef(ar: arInternalRst, priority: subst.priority, - target: newLeaf(subst.mainAnchor[]), + target: newLeaf(subst.target.anchor), info: subst.info, tooltip: "(" & $subst.anchorType & ")") + # find anchors automatically generated from Nim symbols if roNimFile in s.options: - let substNim = findMainAnchorNim(s, signature=text, n.info) + let substNim = findMainAnchorNim(s, signature=alias, n.info) for subst in substNim: foundLinks.add LinkDef(ar: arNim, priority: subst.priority, - target: newLeaf(subst.mainAnchor[]), + target: newLeaf(subst.refname), info: subst.info, tooltip: subst.tooltip) foundLinks.sort(cmp = cmp, order = Descending) - let linkText = addNodes(n) + let aliasStr = addNodes(alias) if foundLinks.len >= 1: let kind = if foundLinks[0].ar == arHyperlink: rnHyperlink elif foundLinks[0].ar == arNim: rnNimdocRef else: rnInternalRef result = newRstNode(kind) - result.sons = @[text, foundLinks[0].target] + result.sons = @[newRstNode(rnInner, desc.sons), foundLinks[0].target] if kind == rnNimdocRef: result.tooltip = foundLinks[0].tooltip if foundLinks.len > 1: # report ambiguous link var targets = newSeq[string]() @@ -3334,10 +3465,10 @@ proc resolveLink(s: PRstSharedState, n: PRstNode) : PRstNode = targets.add t rstMessage(s.filenames, s.msgHandler, n.info, mwAmbiguousLink, "`$1`\n clash:\n$2" % [ - linkText, targets.join("\n")]) + aliasStr, targets.join("\n")]) else: # nothing found result = n - rstMessage(s.filenames, s.msgHandler, n.info, mwBrokenLink, linkText) + rstMessage(s.filenames, s.msgHandler, n.info, mwBrokenLink, aliasStr) proc resolveSubs*(s: PRstSharedState, n: PRstNode): PRstNode = ## Makes pass 2 of RST parsing. @@ -3357,16 +3488,7 @@ proc resolveSubs*(s: PRstSharedState, n: PRstNode): PRstNode = if e != "": result = newLeaf(e) else: rstMessage(s.filenames, s.msgHandler, n.info, mwUnknownSubstitution, key) - of rnHeadline, rnOverline: - # fix up section levels depending on presence of a title and subtitle - if s.hTitleCnt == 2: - if n.level == 1: # it's the subtitle - n.level = 0 - elif n.level >= 2: # normal sections - n.level -= 1 - elif s.hTitleCnt == 0: - n.level += 1 - of rnRef: + of rnRstRef, rnPandocRef: result = resolveLink(s, n) of rnFootnote: var (fnType, num) = getFootnoteType(n.sons[0]) @@ -3416,14 +3538,12 @@ proc resolveSubs*(s: PRstSharedState, n: PRstNode): PRstNode = # TODO: correctly report ambiguities let anchorInfo = findMainAnchorRst(s, refn, n.info) if anchorInfo.len != 0: - result.add newLeaf(anchorInfo[0].mainAnchor[]) # add link + result.add newLeaf(anchorInfo[0].target.anchor) # add link else: rstMessage(s.filenames, s.msgHandler, n.info, mwBrokenLink, refn) result.add newLeaf(refn) # add link of rnLeaf: discard - of rnContents: - s.hasToc = true else: var regroup = false for i in 0 ..< n.len: @@ -3455,7 +3575,8 @@ proc rstParse*(text, filename: string, ## note that 2nd tuple element should be fed to `initRstGenerator` ## argument `filenames` (it is being filled here at least with `filename` ## and possibly with other files from RST ``.. include::`` statement). - var sharedState = newRstSharedState(options, filename, findFile, msgHandler) + var sharedState = newRstSharedState(options, filename, findFile, + msgHandler, hasToc=false) let unresolved = rstParsePass1(text, line, column, sharedState) preparePass2(sharedState, unresolved) result.node = resolveSubs(sharedState, unresolved) diff --git a/lib/packages/docutils/rstast.nim b/lib/packages/docutils/rstast.nim index 1d5da5e1ccf9..e85bbfb98194 100644 --- a/lib/packages/docutils/rstast.nim +++ b/lib/packages/docutils/rstast.nim @@ -11,6 +11,10 @@ import strutils, json +when defined(nimPreviewSlimSystem): + import std/assertions + + type RstNodeKind* = enum ## the possible node kinds of an PRstNode rnInner, # an inner node or a root @@ -23,7 +27,7 @@ type rnBulletItem, # a bullet item rnEnumList, # an enumerated list rnEnumItem, # an enumerated item - rnDefList, # a definition list + rnDefList, rnMdDefList, # a definition list (RST/Markdown) rnDefItem, # an item of a definition list consisting of ... rnDefName, # ... a name part ... rnDefBody, # ... and a body part ... @@ -45,7 +49,10 @@ type rnCitation, # similar to footnote, so use rnFootnote instead rnFootnoteGroup, # footnote group - exists for a purely stylistic # reason: to display a few footnotes as 1 block - rnStandaloneHyperlink, rnHyperlink, rnRef, rnInternalRef, rnFootnoteRef, + rnStandaloneHyperlink, rnHyperlink, + rnRstRef, # RST reference like `section name`_ + rnPandocRef, # Pandoc Markdown reference like [section name] + rnInternalRef, rnFootnoteRef, rnNimdocRef, # reference to automatically generated Nim symbol rnDirective, # a general directive rnDirArg, # a directive argument (for some directives). @@ -106,12 +113,18 @@ type ## auto-numbered ones without a label) of rnMarkdownBlockQuoteItem: quotationDepth*: int ## number of characters in line prefix - of rnRef, rnSubstitutionReferences, + of rnRstRef, rnPandocRef, rnSubstitutionReferences, rnInterpretedText, rnField, rnInlineCode, rnCodeBlock, rnFootnoteRef: info*: TLineInfo ## To have line/column info for warnings at ## nodes that are post-processed after parsing of rnNimdocRef: tooltip*: string + of rnTable, rnGridTable, rnMarkdownTable: + colCount*: int ## Number of (not-united) cells in the table + of rnTableRow: + endsHeader*: bool ## Is last row in the header of table? + of rnTableHeaderCell, rnTableDataCell: + span*: int ## Number of table columns that the cell occupies else: discard anchor*: string ## anchor, internal link target @@ -271,7 +284,7 @@ proc renderRstToRst(d: var RenderContext, n: PRstNode, result: var string) = inc(d.indent, 2) renderRstSons(d, n, result) dec(d.indent, 2) - of rnRef: + of rnRstRef: result.add("`") renderRstSons(d, n, result) result.add("`_") @@ -416,6 +429,13 @@ proc treeRepr*(node: PRstNode, indent=0): string = result.add (if node.order == 0: "" else: " order=" & $node.order) of rnMarkdownBlockQuoteItem: result.add " quotationDepth=" & $node.quotationDepth + of rnTable, rnGridTable, rnMarkdownTable: + result.add " colCount=" & $node.colCount + of rnTableHeaderCell, rnTableDataCell: + if node.span > 0: + result.add " span=" & $node.span + of rnTableRow: + if node.endsHeader: result.add " endsHeader" else: discard result.add (if node.anchor == "": "" else: " anchor='" & node.anchor & "'") diff --git a/lib/packages/docutils/rstgen.nim b/lib/packages/docutils/rstgen.nim index d662a667c0ce..f5ff9aa03e3f 100644 --- a/lib/packages/docutils/rstgen.nim +++ b/lib/packages/docutils/rstgen.nim @@ -37,11 +37,16 @@ ## ## * The same goes for footnotes/citations links: they point to themselves. ## No backreferences are generated since finding all references of a footnote -## can be done by simply searching for [footnoteName]. +## can be done by simply searching for ``[footnoteName]``. import strutils, os, hashes, strtabs, rstast, rst, highlite, tables, sequtils, algorithm, parseutils, std/strbasics + +when defined(nimPreviewSlimSystem): + import std/[assertions, syncio, formatfloat] + + import ../../std/private/since const @@ -53,10 +58,6 @@ type outHtml, # output is HTML outLatex # output is Latex - TocEntry = object - n*: PRstNode - refname*, header*: string - MetaEnum* = enum metaNone, metaTitle, metaSubtitle, metaAuthor, metaVersion @@ -69,7 +70,7 @@ type config*: StringTableRef splitAfter*: int # split too long entries in the TOC listingCounter*: int - tocPart*: seq[TocEntry] + tocPart*: seq[PRstNode] # headings for Table of Contents hasToc*: bool theIndex: string # Contents of the index file to be dumped at the end. findFile*: FindFileHandler @@ -115,7 +116,8 @@ proc initRstGenerator*(g: var RstGenerator, target: OutputTarget, config: StringTableRef, filename: string, findFile: FindFileHandler = nil, msgHandler: MsgHandler = nil, - filenames = default(RstFileTable)) = + filenames = default(RstFileTable), + hasToc = false) = ## Initializes a ``RstGenerator``. ## ## You need to call this before using a ``RstGenerator`` with any other @@ -160,6 +162,7 @@ proc initRstGenerator*(g: var RstGenerator, target: OutputTarget, g.config = config g.target = target g.tocPart = @[] + g.hasToc = hasToc g.filename = filename g.filenames = filenames g.splitAfter = 20 @@ -414,13 +417,13 @@ proc renderIndexTerm*(d: PDoc, n: PRstNode, result: var string) = [id, term]) type - IndexEntry = object - keyword: string - link: string - linkTitle: string ## contains a prettier text for the href - linkDesc: string ## the title attribute of the final href + IndexEntry* = object + keyword*: string + link*: string + linkTitle*: string ## contains a prettier text for the href + linkDesc*: string ## the title attribute of the final href - IndexedDocs = Table[IndexEntry, seq[IndexEntry]] ## \ + IndexedDocs* = Table[IndexEntry, seq[IndexEntry]] ## \ ## Contains the index sequences for doc types. ## ## The key is a *fake* IndexEntry which will contain the title of the @@ -622,7 +625,7 @@ proc generateModuleJumps(modules: seq[string]): string = result.add(chunks.join(", ") & ".
    ") -proc readIndexDir(dir: string): +proc readIndexDir*(dir: string): tuple[modules: seq[string], symbols: seq[IndexEntry], docs: IndexedDocs] = ## Walks `dir` reading ``.idx`` files converting them in IndexEntry items. ## @@ -688,8 +691,6 @@ proc readIndexDir(dir: string): title.linkTitle = "doc_toc_" & $result.docs.len result.docs[title] = fileEntries - sort(result.modules, system.cmp) - proc mergeIndexes*(dir: string): string = ## Merges all index files in `dir` and returns the generated index as HTML. ## @@ -719,6 +720,7 @@ proc mergeIndexes*(dir: string): string = ## Returns the merged and sorted indices into a single HTML block which can ## be further embedded into nimdoc templates. var (modules, symbols, docs) = readIndexDir(dir) + sort(modules, system.cmp) result = "" # Generate a quick jump list of documents. @@ -768,31 +770,18 @@ proc renderHeadline(d: PDoc, n: PRstNode, result: var string) = var tmp = "" for i in countup(0, len(n) - 1): renderRstToOut(d, n.sons[i], tmp) d.currentSection = tmp - # Find the last higher level section for unique reference name - var sectionPrefix = "" - for i in countdown(d.tocPart.high, 0): - let n2 = d.tocPart[i].n - if n2.level < n.level: - sectionPrefix = rstnodeToRefname(n2) & "-" - break - var refname = sectionPrefix & rstnodeToRefname(n) var tocName = esc(d.target, renderRstToText(n), escMode = emOption) # for Latex: simple text without commands that may break TOC/hyperref if d.hasToc: - var length = len(d.tocPart) - setLen(d.tocPart, length + 1) - d.tocPart[length].refname = refname - d.tocPart[length].n = n - d.tocPart[length].header = tmp - + d.tocPart.add n dispA(d.target, result, "\n$3", "\\rsth$4[$6]{$3}$2\n", - [$n.level, refname.idS, tmp, - $chr(n.level - 1 + ord('A')), refname, tocName]) + [$n.level, n.anchor.idS, tmp, + $chr(n.level - 1 + ord('A')), n.anchor, tocName]) else: dispA(d.target, result, "\n$3", "\\rsth$4[$5]{$3}$2\n", [ - $n.level, refname.idS, tmp, + $n.level, n.anchor.idS, tmp, $chr(n.level - 1 + ord('A')), tocName]) # Generate index entry using spaces to indicate TOC level for the output HTML. @@ -805,7 +794,7 @@ proc renderHeadline(d: PDoc, n: PRstNode, result: var string) = # outDir = /foo -\ # destFile = /foo/bar/zoo.html -|-> bar/zoo.html d.destFile.relativePath(d.outDir, '/') - setIndexTerm(d, htmlFileRelPath, refname, tmp.stripTocHtml, + setIndexTerm(d, htmlFileRelPath, n.anchor, tmp.stripTocHtml, spaces(max(0, n.level)) & tmp) proc renderOverline(d: PDoc, n: PRstNode, result: var string) = @@ -824,18 +813,20 @@ proc renderOverline(d: PDoc, n: PRstNode, result: var string) = var tocName = esc(d.target, renderRstToText(n), escMode=emOption) dispA(d.target, result, "
    $3
    ", "\\rstov$4[$5]{$3}$2\n", [$n.level, - rstnodeToRefname(n).idS, tmp, $chr(n.level - 1 + ord('A')), tocName]) + n.anchor.idS, tmp, $chr(n.level - 1 + ord('A')), tocName]) -proc renderTocEntry(d: PDoc, e: TocEntry, result: var string) = +proc renderTocEntry(d: PDoc, n: PRstNode, result: var string) = + var header = "" + for i in countup(0, len(n) - 1): renderRstToOut(d, n.sons[i], header) dispA(d.target, result, "
  • $2
  • \n", - "\\item\\label{$1_toc} $2\\ref{$1}\n", [e.refname, e.header]) + "\\item\\label{$1_toc} $2\\ref{$1}\n", [n.anchor, header]) proc renderTocEntries*(d: var RstGenerator, j: var int, lvl: int, result: var string) = var tmp = "" while j <= high(d.tocPart): - var a = abs(d.tocPart[j].n.level) + var a = abs(d.tocPart[j].level) if a == lvl: renderTocEntry(d, d.tocPart[j], tmp) inc(j) @@ -1074,7 +1065,7 @@ proc renderCode(d: PDoc, n: PRstNode, result: var string) = blockEnd = "}" dispA(d.target, result, blockStart, blockStart, []) if params.lang == langNone: - if len(params.langStr) > 0: + if len(params.langStr) > 0 and params.langStr.toLowerAscii != "none": rstMessage(d.filenames, d.msgHandler, n.info, mwUnsupportedLanguage, params.langStr) for letter in m.text: escChar(d.target, result, letter, emText) @@ -1091,10 +1082,6 @@ proc renderContainer(d: PDoc, n: PRstNode, result: var string) = else: dispA(d.target, result, "
    $2
    ", "$2", [arg, tmp]) -proc texColumns(n: PRstNode): string = - let nColumns = if n.sons.len > 0: len(n.sons[0]) else: 1 - result = "L".repeat(nColumns) - proc renderField(d: PDoc, n: PRstNode, result: var string) = var b = false if d.target == outLatex: @@ -1225,7 +1212,7 @@ proc renderRstToOut(d: PDoc, n: PRstNode, result: var string) = of rnBulletItem, rnEnumItem: renderAux(d, n, "$1\n", "\\item $2$1\n", result) of rnEnumList: renderEnumList(d, n, result) - of rnDefList: + of rnDefList, rnMdDefList: renderAux(d, n, "$1\n", "\\begin{description}\n$2\n$1\\end{description}\n", result) of rnDefItem: renderAux(d, n, result) @@ -1323,24 +1310,49 @@ proc renderRstToOut(d: PDoc, n: PRstNode, result: var string) = renderAux(d, n, "$1", "\n$2\n\\begin{rsttab}{" & - texColumns(n) & "}\n\\hline\n$1\\end{rsttab}", result) + "L".repeat(n.colCount) & "}\n\\toprule\n$1" & + "\\addlinespace[0.1em]\\bottomrule\n\\end{rsttab}", result) of rnTableRow: if len(n) >= 1: - if d.target == outLatex: - #var tmp = "" - renderRstToOut(d, n.sons[0], result) - for i in countup(1, len(n) - 1): - result.add(" & ") - renderRstToOut(d, n.sons[i], result) - result.add("\\\\\n\\hline\n") - else: + case d.target + of outHtml: result.add("") renderAux(d, n, result) result.add("\n") - of rnTableDataCell: - renderAux(d, n, "$1", "$1", result) - of rnTableHeaderCell: - renderAux(d, n, "$1", "\\textbf{$1}", result) + of outLatex: + if n.sons[0].kind == rnTableHeaderCell: + result.add "\\rowcolor{gray!15} " + var spanLines: seq[(int, int)] + var nCell = 0 + for uCell in 0 .. n.len - 1: + renderRstToOut(d, n.sons[uCell], result) + if n.sons[uCell].span > 0: + spanLines.add (nCell + 1, nCell + n.sons[uCell].span) + nCell += n.sons[uCell].span + else: + nCell += 1 + if uCell != n.len - 1: + result.add(" & ") + result.add("\\\\") + if n.endsHeader: result.add("\\midrule\n") + for (start, stop) in spanLines: + result.add("\\cmidrule(lr){$1-$2}" % [$start, $stop]) + result.add("\n") + of rnTableHeaderCell, rnTableDataCell: + case d.target + of outHtml: + let tag = if n.kind == rnTableHeaderCell: "th" else: "td" + var spanSpec: string + if n.span <= 1: spanSpec = "" + else: + spanSpec = " colspan=\"" & $n.span & "\" style=\"text-align: center\"" + renderAux(d, n, "<$1$2>$$1" % [tag, spanSpec], "", result) + of outLatex: + let text = if n.kind == rnTableHeaderCell: "\\textbf{$1}" else: "$1" + var latexStr: string + if n.span <= 1: latexStr = text + else: latexStr = "\\multicolumn{" & $n.span & "}{c}{" & text & "}" + renderAux(d, n, "", latexStr, result) of rnFootnoteGroup: renderAux(d, n, "
    " & @@ -1359,7 +1371,9 @@ proc renderRstToOut(d: PDoc, n: PRstNode, result: var string) = "
      $1\n
    \n", "\\item[\\textsuperscript{[$3]}]$2 $1\n", [body, n.anchor.idS, mark, n.anchor]) - of rnRef: + of rnPandocRef: + renderHyperlink(d, text=n.sons[0], link=n.sons[1], result, external=false) + of rnRstRef: renderHyperlink(d, text=n.sons[0], link=n.sons[0], result, external=false) of rnStandaloneHyperlink: renderHyperlink(d, text=n.sons[0], link=n.sons[0], result, external=true) @@ -1604,11 +1618,12 @@ proc rstToHtml*(s: string, options: RstParseOptions, result = "" const filen = "input" - let (rst, filenames, _) = rstParse(s, filen, + let (rst, filenames, t) = rstParse(s, filen, line=LineRstInit, column=ColRstInit, options, myFindFile, msgHandler) var d: RstGenerator - initRstGenerator(d, outHtml, config, filen, myFindFile, msgHandler, filenames) + initRstGenerator(d, outHtml, config, filen, myFindFile, msgHandler, + filenames, hasToc = t) result = "" renderRstToOut(d, rst, result) strbasics.strip(result) @@ -1618,10 +1633,11 @@ proc rstToLatex*(rstSource: string; options: RstParseOptions): string {.inline, ## Convenience proc for `renderRstToOut` and `initRstGenerator`. runnableExamples: doAssert rstToLatex("*Hello* **world**", {}) == """\emph{Hello} \textbf{world}""" if rstSource.len == 0: return - let (rst, filenames, _) = rstParse(rstSource, "", + let (rst, filenames, t) = rstParse(rstSource, "", line=LineRstInit, column=ColRstInit, options) var rstGenera: RstGenerator - rstGenera.initRstGenerator(outLatex, defaultConfig(), "input", filenames=filenames) + rstGenera.initRstGenerator(outLatex, defaultConfig(), "input", + filenames=filenames, hasToc = t) rstGenera.renderRstToOut(rst, result) strbasics.strip(result) diff --git a/lib/posix/epoll.nim b/lib/posix/epoll.nim index 1f105ecac257..7ee062e4fb99 100644 --- a/lib/posix/epoll.nim +++ b/lib/posix/epoll.nim @@ -33,19 +33,28 @@ const EPOLL_CTL_DEL* = 2 # Remove a file descriptor from the interface. EPOLL_CTL_MOD* = 3 # Change file descriptor epoll_event structure. +# https://github.com/torvalds/linux/blob/ff6992735ade75aae3e35d16b17da1008d753d28/include/uapi/linux/eventpoll.h#L77 +when defined(linux) and defined(amd64): + {.pragma: epollPacked, packed.} +else: + {.pragma: epollPacked.} + type - EpollData* {.importc: "union epoll_data", - header: "", pure, final.} = object # TODO: This is actually a union. + EpollData* {.importc: "epoll_data_t", + header: "", pure, final, union.} = object + `ptr`* {.importc: "ptr".}: pointer + fd* {.importc: "fd".}: cint + u32* {.importc: "u32".}: uint32 u64* {.importc: "u64".}: uint64 - EpollEvent* {.importc: "struct epoll_event", header: "", pure, final.} = object + EpollEvent* {.importc: "struct epoll_event", header: "", pure, final, epollPacked.} = object events*: uint32 # Epoll events data*: EpollData # User data variable proc epoll_create*(size: cint): cint {.importc: "epoll_create", header: "".} ## Creates an epoll instance. Returns an fd for the new instance. - ## + ## ## The "size" parameter is a hint specifying the number of file ## descriptors to be associated with the new instance. The fd ## returned by epoll_create() should be closed with close(). @@ -59,7 +68,7 @@ proc epoll_ctl*(epfd: cint; op: cint; fd: cint | SocketHandle; event: ptr EpollE importc: "epoll_ctl", header: "".} ## Manipulate an epoll instance "epfd". Returns `0` in case of success, ## `-1` in case of error (the "errno" variable will contain the specific error code). - ## + ## ## The "op" parameter is one of the `EPOLL_CTL_*` ## constants defined above. The "fd" parameter is the target of the ## operation. The "event" parameter describes which events the caller diff --git a/lib/posix/posix.nim b/lib/posix/posix.nim index 146ba886f9ce..4ebae4361877 100644 --- a/lib/posix/posix.nim +++ b/lib/posix/posix.nim @@ -894,19 +894,9 @@ proc CMSG_NXTHDR*(mhdr: ptr Tmsghdr, cmsg: ptr Tcmsghdr): ptr Tcmsghdr {. proc CMSG_FIRSTHDR*(mhdr: ptr Tmsghdr): ptr Tcmsghdr {. importc, header: "".} -{.push warning[deprecated]: off.} -proc CMSG_SPACE*(len: csize): csize {. - importc, header: "", deprecated: "argument `len` should be of type `csize_t`".} -{.pop.} - proc CMSG_SPACE*(len: csize_t): csize_t {. importc, header: "".} -{.push warning[deprecated]: off.} -proc CMSG_LEN*(len: csize): csize {. - importc, header: "", deprecated: "argument `len` should be of type `csize_t`".} -{.pop.} - proc CMSG_LEN*(len: csize_t): csize_t {. importc, header: "".} diff --git a/lib/posix/posix_freertos_consts.nim b/lib/posix/posix_freertos_consts.nim index ca500534a02d..0f0fc0aae47f 100644 --- a/lib/posix/posix_freertos_consts.nim +++ b/lib/posix/posix_freertos_consts.nim @@ -500,3 +500,7 @@ const F_TEST* = cint(3) const F_TLOCK* = cint(2) const F_ULOCK* = cint(0) +# +const SEEK_SET* = cint(0) +const SEEK_CUR* = cint(1) +const SEEK_END* = cint(2) diff --git a/lib/posix/posix_haiku.nim b/lib/posix/posix_haiku.nim index d626b210605f..32a6d24e26a3 100644 --- a/lib/posix/posix_haiku.nim +++ b/lib/posix/posix_haiku.nim @@ -304,7 +304,7 @@ type Stack* {.importc: "stack_t", header: "", final, pure.} = object ## stack_t ss_sp*: pointer ## Stack base or pointer. - ss_size*: csize ## Stack size. + ss_size*: csize_t ## Stack size. ss_flags*: cint ## Flags. SigInfo* {.importc: "siginfo_t", @@ -404,7 +404,7 @@ type IOVec* {.importc: "struct iovec", pure, final, header: "".} = object ## struct iovec iov_base*: pointer ## Base address of a memory region for input or output. - iov_len*: csize ## The size of the memory pointed to by iov_base. + iov_len*: csize_t ## The size of the memory pointed to by iov_base. Tmsghdr* {.importc: "struct msghdr", pure, final, header: "".} = object ## struct msghdr diff --git a/lib/posix/posix_linux_amd64.nim b/lib/posix/posix_linux_amd64.nim index 2309e19a973b..4eb357d62030 100644 --- a/lib/posix/posix_linux_amd64.nim +++ b/lib/posix/posix_linux_amd64.nim @@ -168,7 +168,7 @@ type Pthread_key* {.importc: "pthread_key_t", header: "".} = cuint Pthread_mutex* {.importc: "pthread_mutex_t", header: "", pure, final.} = object - abi: array[48 div sizeof(clong), clong] + abi: array[40 div sizeof(clong), clong] Pthread_mutexattr* {.importc: "pthread_mutexattr_t", header: "", pure, final.} = object abi: array[4 div sizeof(cint), cint] @@ -428,8 +428,8 @@ type header: "", pure, final.} = object ## struct sockaddr_storage ss_family*: TSa_Family ## Address family. - ss_padding: array[128 - sizeof(cshort) - sizeof(culong), char] - ss_align: clong + ss_padding {.importc: "__ss_padding".}: array[128 - sizeof(cshort) - sizeof(culong), char] + ss_align {.importc: "__ss_align".}: clong Tif_nameindex* {.importc: "struct if_nameindex", final, pure, header: "".} = object ## struct if_nameindex diff --git a/lib/posix/posix_nintendoswitch.nim b/lib/posix/posix_nintendoswitch.nim index 4cef80bb739b..b66563695c1e 100644 --- a/lib/posix/posix_nintendoswitch.nim +++ b/lib/posix/posix_nintendoswitch.nim @@ -286,7 +286,7 @@ type header: "", final, pure.} = object ## stack_t ss_sp*: pointer ## Stack base or pointer. ss_flags*: cint ## Flags. - ss_size*: csize ## Stack size. + ss_size*: csize_t ## Stack size. SigInfo* {.importc: "siginfo_t", header: "", final, pure.} = object ## siginfo_t @@ -321,7 +321,7 @@ type aio_lio_opcode*: cint ## Operation to be performed. aio_reqprio*: cint ## Request priority offset. aio_buf*: pointer ## Location of buffer. - aio_nbytes*: csize ## Length of transfer. + aio_nbytes*: csize_t ## Length of transfer. aio_sigevent*: SigEvent ## Signal number and value. next_prio: pointer abs_prio: cint @@ -378,15 +378,15 @@ type msg_name*: pointer ## Optional address. msg_namelen*: SockLen ## Size of address. msg_iov*: ptr IOVec ## Scatter/gather array. - msg_iovlen*: csize ## Members in msg_iov. + msg_iovlen*: csize_t ## Members in msg_iov. msg_control*: pointer ## Ancillary data; see below. - msg_controllen*: csize ## Ancillary data buffer len. + msg_controllen*: csize_t ## Ancillary data buffer len. msg_flags*: cint ## Flags on received message. Tcmsghdr* {.importc: "struct cmsghdr", pure, final, header: "".} = object ## struct cmsghdr - cmsg_len*: csize ## Data byte count, including the cmsghdr. + cmsg_len*: csize_t ## Data byte count, including the cmsghdr. cmsg_level*: cint ## Originating protocol. cmsg_type*: cint ## Protocol-specific type. diff --git a/lib/pure/algorithm.nim b/lib/pure/algorithm.nim index c43545f78900..fc0eceac3c8e 100644 --- a/lib/pure/algorithm.nim +++ b/lib/pure/algorithm.nim @@ -825,10 +825,10 @@ proc rotateLeft*[T](arg: var openArray[T]; slice: HSlice[int, int]; ## If an invalid range (`HSlice`) is passed, it raises `IndexDefect`. ## ## `slice` - ## The indices of the element range that should be rotated. + ## : The indices of the element range that should be rotated. ## ## `dist` - ## The distance in amount of elements that the data should be rotated. + ## : The distance in amount of elements that the data should be rotated. ## Can be negative, can be any number. ## ## **See also:** @@ -876,10 +876,10 @@ proc rotatedLeft*[T](arg: openArray[T]; slice: HSlice[int, int], ## If an invalid range (`HSlice`) is passed, it raises `IndexDefect`. ## ## `slice` - ## The indices of the element range that should be rotated. + ## : The indices of the element range that should be rotated. ## ## `dist` - ## The distance in amount of elements that the data should be rotated. + ## : The distance in amount of elements that the data should be rotated. ## Can be negative, can be any number. ## ## **See also:** diff --git a/lib/pure/asyncdispatch.nim b/lib/pure/asyncdispatch.nim index b1e1c19fd247..92ad9c5ffcd5 100644 --- a/lib/pure/asyncdispatch.nim +++ b/lib/pure/asyncdispatch.nim @@ -98,6 +98,10 @@ ## `await`. The following section shows different ways that you can handle ## exceptions in async procs. ## +## .. caution:: +## Procedures marked {.async.} do not support mutable parameters such +## as `var int`. References such as `ref int` should be used instead. +## ## Handling Exceptions ## ------------------- ## @@ -123,15 +127,42 @@ ## if future.failed: ## # Handle exception ## -## ## Discarding futures ## ================== ## -## 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. Note -## however that this does not wait for completion, and you should use -## `waitFor` for that purpose. +## Futures should **never** be discarded directly 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. Note that this +## does not wait for completion, and you should use `waitFor` or `await` for that purpose. +## +## .. note:: `await` also checks if the future fails, so you can safely discard +## its result. +## +## Handling futures +## ================ +## +## There are many different operations that apply to a future. +## The three primary high-level operations are `asyncCheck`, +## `waitFor`, and `await`. +## +## * `asyncCheck`: Raises an exception if the future fails. It neither waits +## for the future to finish nor returns the result of the future. +## * `waitFor`: Polls the event loop and blocks the current thread until the +## future finishes. This is often used to call an async procedure from a +## synchronous context and should never be used in an `async` proc. +## * `await`: Pauses execution in the current async procedure until the future +## finishes. While the current procedure is paused, other async procedures will +## continue running. Should be used instead of `waitFor` in an async +## procedure. +## +## Here is a handy quick reference chart showing their high-level differences: +## ============== ===================== ======================= +## Procedure Context Blocking +## ============== ===================== ======================= +## `asyncCheck` non-async and async non-blocking +## `waitFor` non-async blocks current thread +## `await` async suspends current proc +## ============== ===================== ======================= ## ## Examples ## ======== @@ -165,6 +196,7 @@ ## ================ ## ## * The effect system (`raises: []`) does not work with async procedures. +## * Mutable parameters are not supported by async procedures. ## ## ## Multiple async backend support @@ -200,6 +232,9 @@ import asyncfutures except callSoon import nativesockets, net, deques +when defined(nimPreviewSlimSystem): + import std/[assertions, syncio] + export Port, SocketFlag export asyncfutures except callSoon export asyncstreams @@ -1701,6 +1736,8 @@ when defined(windows) or defined(nimdoc): proc (fd: AsyncFD, bytesCount: DWORD, errcode: OSErrorCode) = if not retFuture.finished: if errcode == OSErrorCode(-1): + const SO_UPDATE_CONNECT_CONTEXT = 0x7010 + socket.SocketHandle.setSockOptInt(SOL_SOCKET, SO_UPDATE_CONNECT_CONTEXT, 1) # 15022 retFuture.complete() else: retFuture.fail(newException(OSError, osErrorMsg(errcode))) diff --git a/lib/pure/asyncfile.nim b/lib/pure/asyncfile.nim index 222a89b97bd8..9cc9f5b4895f 100644 --- a/lib/pure/asyncfile.nim +++ b/lib/pure/asyncfile.nim @@ -24,6 +24,9 @@ import asyncdispatch, os +when defined(nimPreviewSlimSystem): + import std/[assertions, syncio] + # TODO: Fix duplication introduced by PR #4683. when defined(windows) or defined(nimdoc): diff --git a/lib/pure/asyncftpclient.nim b/lib/pure/asyncftpclient.nim index 056d6556416a..0d2ee80c5e4a 100644 --- a/lib/pure/asyncftpclient.nim +++ b/lib/pure/asyncftpclient.nim @@ -81,6 +81,9 @@ import asyncdispatch, asyncnet, nativesockets, strutils, parseutils, os, times from net import BufferSize +when defined(nimPreviewSlimSystem): + import std/assertions + type AsyncFtpClient* = ref object csock*: AsyncSocket diff --git a/lib/pure/asyncfutures.nim b/lib/pure/asyncfutures.nim index 4782f0a7381d..035b6182da73 100644 --- a/lib/pure/asyncfutures.nim +++ b/lib/pure/asyncfutures.nim @@ -11,6 +11,10 @@ import os, tables, strutils, times, heapqueue, options, deques, cstrutils import system/stacktraces +when defined(nimPreviewSlimSystem): + import std/objectdollar # for StackTraceEntry + import std/assertions + # TODO: This shouldn't need to be included, but should ideally be exported. type CallbackFunc = proc () {.closure, gcsafe.} diff --git a/lib/pure/asynchttpserver.nim b/lib/pure/asynchttpserver.nim index d7daacd03ffa..6694c4bc2fed 100644 --- a/lib/pure/asynchttpserver.nim +++ b/lib/pure/asynchttpserver.nim @@ -44,6 +44,9 @@ import httpcore from nativesockets import getLocalAddr, Domain, AF_INET, AF_INET6 import std/private/since +when defined(nimPreviewSlimSystem): + import std/assertions + export httpcore except parseHeader const @@ -170,7 +173,7 @@ proc processRequest( server: AsyncHttpServer, req: FutureVar[Request], client: AsyncSocket, - address: string, + address: sink string, lineFut: FutureVar[string], callback: proc (request: Request): Future[void] {.closure, gcsafe.}, ): Future[bool] {.async.} = @@ -184,7 +187,10 @@ proc processRequest( # \n request.headers.clear() request.body = "" - request.hostname.shallowCopy(address) + when defined(gcArc) or defined(gcOrc): + request.hostname = address + else: + request.hostname.shallowCopy(address) assert client != nil request.client = client diff --git a/lib/pure/asyncmacro.nim b/lib/pure/asyncmacro.nim index 63c8e6e5c798..d85e3e621b66 100644 --- a/lib/pure/asyncmacro.nim +++ b/lib/pure/asyncmacro.nim @@ -126,7 +126,9 @@ template await*(f: typed): untyped {.used.} = error "await expects Future[T], got " & $typeof(f) template await*[T](f: Future[T]): auto {.used.} = - template yieldFuture = yield FutureBase() + when not defined(nimHasTemplateRedefinitionPragma): + {.pragma: redefine.} + template yieldFuture {.redefine.} = yield FutureBase() when compiles(yieldFuture): var internalTmpFuture: FutureBase = f diff --git a/lib/pure/asyncnet.nim b/lib/pure/asyncnet.nim index 61c2fc75acac..b61eaa9024c7 100644 --- a/lib/pure/asyncnet.nim +++ b/lib/pure/asyncnet.nim @@ -96,6 +96,10 @@ ## import std/private/since + +when defined(nimPreviewSlimSystem): + import std/[assertions, syncio] + import asyncdispatch, nativesockets, net, os export SOBool @@ -399,7 +403,8 @@ proc recv*(socket: AsyncSocket, size: int, ## to be read then the future will complete with a value of `""`. if socket.isBuffered: result = newString(size) - shallow(result) + when not defined(nimSeqsV2): + shallow(result) let originalBufPos = socket.currPos if socket.bufLen == 0: diff --git a/lib/pure/asyncstreams.nim b/lib/pure/asyncstreams.nim index 083c6f0eab18..3f7774ed8661 100644 --- a/lib/pure/asyncstreams.nim +++ b/lib/pure/asyncstreams.nim @@ -11,6 +11,9 @@ import asyncfutures +when defined(nimPreviewSlimSystem): + import std/assertions + import deques type diff --git a/lib/pure/base64.nim b/lib/pure/base64.nim index 9b7af88ef118..bc01a9164905 100644 --- a/lib/pure/base64.nim +++ b/lib/pure/base64.nim @@ -180,7 +180,7 @@ proc encode*(s: string, safe = false): string = assert encode("Hello World") == "SGVsbG8gV29ybGQ=" encodeImpl() -proc encodeMime*(s: string, lineLen = 75, newLine = "\r\n"): string = +proc encodeMime*(s: string, lineLen = 75.Positive, newLine = "\r\n"): string = ## Encodes `s` into base64 representation as lines. ## Used in email MIME format, use `lineLen` and `newline`. ## @@ -191,11 +191,25 @@ proc encodeMime*(s: string, lineLen = 75, newLine = "\r\n"): string = ## * `decode proc<#decode,string>`_ for decoding a string runnableExamples: assert encodeMime("Hello World", 4, "\n") == "SGVs\nbG8g\nV29y\nbGQ=" - result = newStringOfCap(encodeSize(s.len)) - for i, c in encode(s): - if i != 0 and (i mod lineLen == 0): - result.add(newLine) - result.add(c) + template cpy(l, src, idx) = + b = l + while i < b: + result[i] = src[idx] + inc i + inc idx + + if s.len == 0: return + let e = encode(s) + if e.len <= lineLen or newLine.len == 0: + return e + result = newString(e.len + newLine.len * ((e.len div lineLen) - int(e.len mod lineLen == 0))) + var i, j, k, b: int + let nd = e.len - lineLen + while j < nd: + cpy(i + lineLen, e, j) + cpy(i + newLine.len, newLine, k) + k = 0 + cpy(result.len, e, j) proc initDecodeTable*(): array[256, char] = # computes a decode table at compile time diff --git a/lib/pure/cgi.nim b/lib/pure/cgi.nim index 566482b217ab..0ab8f4c95d82 100644 --- a/lib/pure/cgi.nim +++ b/lib/pure/cgi.nim @@ -32,6 +32,9 @@ import strutils, os, strtabs, cookies, uri export uri.encodeUrl, uri.decodeUrl +when defined(nimPreviewSlimSystem): + import std/syncio + proc addXmlChar(dest: var string, c: char) {.inline.} = case c diff --git a/lib/pure/collections/critbits.nim b/lib/pure/collections/critbits.nim index e1298499548d..24257dacb945 100644 --- a/lib/pure/collections/critbits.nim +++ b/lib/pure/collections/critbits.nim @@ -36,6 +36,9 @@ runnableExamples: import std/private/since +when defined(nimPreviewSlimSystem): + import std/assertions + type NodeObj[T] {.acyclic.} = object byte: int ## byte index of the difference @@ -197,7 +200,7 @@ proc missingOrExcl*[T](c: var CritBitTree[T], key: string): bool = discard exclImpl(c, key) result = c.count == oldCount -proc containsOrIncl*[T](c: var CritBitTree[T], key: string, val: T): bool = +proc containsOrIncl*[T](c: var CritBitTree[T], key: string, val: sink T): bool = ## Returns true if `c` contains the given `key`. If the key does not exist, ## `c[key] = val` is performed. ## @@ -270,7 +273,7 @@ proc incl*(c: var CritBitTree[void], key: string) = discard rawInsert(c, key) -proc incl*[T](c: var CritBitTree[T], key: string, val: T) = +proc incl*[T](c: var CritBitTree[T], key: string, val: sink T) = ## Inserts `key` with value `val` into `c`. ## ## **See also:** @@ -284,7 +287,7 @@ proc incl*[T](c: var CritBitTree[T], key: string, val: T) = var n = rawInsert(c, key) n.val = val -proc `[]=`*[T](c: var CritBitTree[T], key: string, val: T) = +proc `[]=`*[T](c: var CritBitTree[T], key: string, val: sink T) = ## Alias for `incl <#incl,CritBitTree[T],string,T>`_. ## ## **See also:** @@ -300,7 +303,7 @@ template get[T](c: CritBitTree[T], key: string): T = n.val -func `[]`*[T](c: CritBitTree[T], key: string): T {.inline.} = +func `[]`*[T](c: CritBitTree[T], key: string): lent T {.inline.} = ## Retrieves the value at `c[key]`. If `key` is not in `t`, the ## `KeyError` exception is raised. One can check with `hasKey` whether ## the key exists. @@ -342,7 +345,7 @@ iterator keys*[T](c: CritBitTree[T]): string = for x in leaves(c.root): yield x.key -iterator values*[T](c: CritBitTree[T]): T = +iterator values*[T](c: CritBitTree[T]): lent T = ## Yields all values of `c` in the lexicographical order of the ## corresponding keys. ## @@ -415,7 +418,7 @@ iterator keysWithPrefix*[T](c: CritBitTree[T], prefix: string): string = let top = allprefixedAux(c, prefix) for x in leaves(top): yield x.key -iterator valuesWithPrefix*[T](c: CritBitTree[T], prefix: string): T = +iterator valuesWithPrefix*[T](c: CritBitTree[T], prefix: string): lent T = ## Yields all values of `c` starting with `prefix` of the ## corresponding keys. ## @@ -518,7 +521,7 @@ func commonPrefixLen*[T](c: CritBitTree[T]): int {.inline, since((1, 3)).} = else: c.root.byte else: 0 -proc toCritBitTree*[T](pairs: openArray[(string, T)]): CritBitTree[T] {.since: (1, 3).} = +proc toCritBitTree*[T](pairs: sink openArray[(string, T)]): CritBitTree[T] {.since: (1, 3).} = ## Creates a new `CritBitTree` that contains the given `pairs`. runnableExamples: doAssert {"a": "0", "b": "1", "c": "2"}.toCritBitTree is CritBitTree[string] @@ -526,7 +529,7 @@ proc toCritBitTree*[T](pairs: openArray[(string, T)]): CritBitTree[T] {.since: ( for item in pairs: result.incl item[0], item[1] -proc toCritBitTree*(items: openArray[string]): CritBitTree[void] {.since: (1, 3).} = +proc toCritBitTree*(items: sink openArray[string]): CritBitTree[void] {.since: (1, 3).} = ## Creates a new `CritBitTree` that contains the given `items`. runnableExamples: doAssert ["a", "b", "c"].toCritBitTree is CritBitTree[void] diff --git a/lib/pure/collections/lists.nim b/lib/pure/collections/lists.nim index d1de0ea67252..829ec2ccb84c 100644 --- a/lib/pure/collections/lists.nim +++ b/lib/pure/collections/lists.nim @@ -59,6 +59,9 @@ runnableExamples: import std/private/since +when defined(nimPreviewSlimSystem): + import std/assertions + when not defined(nimHasCursor): {.pragma: cursor.} diff --git a/lib/pure/collections/sequtils.nim b/lib/pure/collections/sequtils.nim index 5e9b492c2505..b869775398f7 100644 --- a/lib/pure/collections/sequtils.nim +++ b/lib/pure/collections/sequtils.nim @@ -791,7 +791,7 @@ template toSeq1(s: not iterator): untyped = i += 1 result else: - var result: seq[OutType] = @[] + var result: seq[OutType]# = @[] for it in s: result.add(it) result @@ -808,7 +808,7 @@ template toSeq2(iter: iterator): untyped = result else: type OutType = typeof(iter2()) - var result: seq[OutType] = @[] + var result: seq[OutType]# = @[] when compiles(iter2()): evalOnceAs(iter4, iter, false) let iter3 = iter4() @@ -852,7 +852,7 @@ template toSeq*(iter: untyped): untyped = inc i result else: - var result: seq[typeof(iter)] = @[] + var result: seq[typeof(iter)]# = @[] for x in iter: result.add(x) result @@ -914,7 +914,7 @@ template foldl*(sequence, operation, first): untyped = ## ## The `operation` parameter should be an expression which uses the variables ## `a` and `b` for each step of the fold. The `first` parameter is the - ## start value (the first `a`) and therefor defines the type of the result. + ## start value (the first `a`) and therefore defines the type of the result. ## ## **See also:** ## * `foldr template<#foldr.t,untyped,untyped>`_ @@ -1020,7 +1020,7 @@ template mapIt*(s: typed, op: untyped): untyped = i += 1 result else: - var result: seq[OutType] = @[] + var result: seq[OutType]# = @[] # use `items` to avoid https://github.com/nim-lang/Nim/issues/12639 for it {.inject.} in items(s): result.add(op) diff --git a/lib/pure/collections/sets.nim b/lib/pure/collections/sets.nim index 7b1c58ac8cf5..114e4582a905 100644 --- a/lib/pure/collections/sets.nim +++ b/lib/pure/collections/sets.nim @@ -61,7 +61,7 @@ type HashSet*[A] {.myShallow.} = object ## \ ## A generic hash set. ## - ## Use `init proc <#init,HashSet[A]>`_ or `initHashSet proc <#initHashSet,int>`_ + ## Use `init proc <#init,HashSet[A]>`_ or `initHashSet proc <#initHashSet>`_ ## before calling other procs on it. data: KeyValuePairSeq[A] counter: int diff --git a/lib/pure/collections/tables.nim b/lib/pure/collections/tables.nim index 00f71ef1d24b..5c62ed56ae5d 100644 --- a/lib/pure/collections/tables.nim +++ b/lib/pure/collections/tables.nim @@ -917,7 +917,7 @@ proc contains*[A, B](t: TableRef[A, B], key: A): bool = return hasKey[A, B](t, key) -proc hasKeyOrPut*[A, B](t: var TableRef[A, B], key: A, val: B): bool = +proc hasKeyOrPut*[A, B](t: TableRef[A, B], key: A, val: B): bool = ## Returns true if `key` is in the table, otherwise inserts `value`. ## ## See also: @@ -1908,7 +1908,7 @@ proc contains*[A, B](t: OrderedTableRef[A, B], key: A): bool = return hasKey[A, B](t, key) -proc hasKeyOrPut*[A, B](t: var OrderedTableRef[A, B], key: A, val: B): bool = +proc hasKeyOrPut*[A, B](t: OrderedTableRef[A, B], key: A, val: B): bool = ## Returns true if `key` is in the table, otherwise inserts `value`. ## ## See also: diff --git a/lib/pure/complex.nim b/lib/pure/complex.nim index 5492028a8c59..1e8349ef6499 100644 --- a/lib/pure/complex.nim +++ b/lib/pure/complex.nim @@ -81,6 +81,13 @@ func abs2*[T](z: Complex[T]): T = ## that is the squared distance from (0, 0) to `z`. ## This is more efficient than `abs(z) ^ 2`. result = z.re * z.re + z.im * z.im + +func sgn*[T](z: Complex[T]): Complex[T] = + ## Returns the phase of `z` as a unit complex number, + ## or 0 if `z` is 0. + let a = abs(z) + if a != 0: + result = z / a func conjugate*[T](z: Complex[T]): Complex[T] = ## Returns the complex conjugate of `z` (`complex(z.re, -z.im)`). @@ -156,18 +163,7 @@ func `/`*[T](x: T; y: Complex[T]): Complex[T] = func `/`*[T](x, y: Complex[T]): Complex[T] = ## Divides two complex numbers. - var r, den: T - if abs(y.re) < abs(y.im): - r = y.re / y.im - den = y.im + r * y.re - result.re = (x.re * r + x.im) / den - result.im = (x.im * r - x.re) / den - else: - r = y.im / y.re - den = y.re + r * y.im - result.re = (x.re + r * x.im) / den - result.im = (x.im - r * x.re) / den - + x * conjugate(y) / abs2(y) func `+=`*[T](x: var Complex[T]; y: Complex[T]) = ## Adds `y` to `x`. diff --git a/lib/pure/concurrency/atomics.nim b/lib/pure/concurrency/atomics.nim index c0a122bf9501..315f44f338e6 100644 --- a/lib/pure/concurrency/atomics.nim +++ b/lib/pure/concurrency/atomics.nim @@ -286,7 +286,7 @@ else: moSequentiallyConsistent type - # Atomic* {.importcpp: "_Atomic('0)".} [T] = object + # Atomic*[T] {.importcpp: "_Atomic('0)".} = object AtomicInt8 {.importc: "_Atomic NI8".} = int8 AtomicInt16 {.importc: "_Atomic NI16".} = int16 diff --git a/lib/pure/concurrency/cpuload.nim b/lib/pure/concurrency/cpuload.nim index 841d58d86b26..137dd67ad3a6 100644 --- a/lib/pure/concurrency/cpuload.nim +++ b/lib/pure/concurrency/cpuload.nim @@ -19,6 +19,9 @@ when defined(windows): elif defined(linux): from cpuinfo import countProcessors +when defined(nimPreviewSlimSystem): + import std/syncio + type ThreadPoolAdvice* = enum doNothing, diff --git a/lib/pure/concurrency/threadpool.nim b/lib/pure/concurrency/threadpool.nim index 9334b48339c7..5d9e7452ba5f 100644 --- a/lib/pure/concurrency/threadpool.nim +++ b/lib/pure/concurrency/threadpool.nim @@ -23,6 +23,9 @@ when not compileOption("threads"): import cpuinfo, cpuload, locks, os +when defined(nimPreviewSlimSystem): + import std/assertions + {.push stackTrace:off.} type diff --git a/lib/pure/cookies.nim b/lib/pure/cookies.nim index 0306558d6daf..22704e4348f9 100644 --- a/lib/pure/cookies.nim +++ b/lib/pure/cookies.nim @@ -11,6 +11,9 @@ import strtabs, times, options +when defined(nimPreviewSlimSystem): + import std/assertions + type SameSite* {.pure.} = enum ## The SameSite cookie attribute. diff --git a/lib/pure/coro.nim b/lib/pure/coro.nim index f4495a536743..aaf442a83e78 100644 --- a/lib/pure/coro.nim +++ b/lib/pure/coro.nim @@ -8,11 +8,11 @@ # ## Nim coroutines implementation, supports several context switching methods: -## -------- ------------ +## ======== ============ ## ucontext available on unix and alike (default) ## setjmp available on unix and alike (x86/64 only) ## fibers available and required on windows. -## -------- ------------ +## ======== ============ ## ## -d:nimCoroutines Required to build this module. ## -d:nimCoroutinesUcontext Use ucontext backend. diff --git a/lib/pure/encodings.nim b/lib/pure/encodings.nim index 36e891dd38d4..26b6e1f221e5 100644 --- a/lib/pure/encodings.nim +++ b/lib/pure/encodings.nim @@ -40,6 +40,8 @@ runnableExamples: import os +when defined(nimPreviewSlimSystem): + import std/assertions when not defined(windows): type diff --git a/lib/pure/future.nim b/lib/pure/future.nim deleted file mode 100644 index 40dba78469de..000000000000 --- a/lib/pure/future.nim +++ /dev/null @@ -1,4 +0,0 @@ - -{.deprecated: "Use the new 'sugar' module instead".} - -include sugar diff --git a/lib/pure/httpclient.nim b/lib/pure/httpclient.nim index 5b6e439dbddd..5c7f538b5d90 100644 --- a/lib/pure/httpclient.nim +++ b/lib/pure/httpclient.nim @@ -19,7 +19,10 @@ ## .. code-block:: Nim ## import std/httpclient ## var client = newHttpClient() -## echo client.getContent("http://google.com") +## try: +## echo client.getContent("http://google.com") +## finally: +## client.close() ## ## The same action can also be performed asynchronously, simply use the ## `AsyncHttpClient`: @@ -29,7 +32,10 @@ ## ## proc asyncProc(): Future[string] {.async.} = ## var client = newAsyncHttpClient() -## return await client.getContent("http://example.com") +## try: +## return await client.getContent("http://example.com") +## finally: +## client.close() ## ## echo waitFor asyncProc() ## @@ -53,8 +59,10 @@ ## data["output"] = "soap12" ## data["uploaded_file"] = ("test.html", "text/html", ## "

    test

    ") -## -## echo client.postContent("http://validator.w3.org/check", multipart=data) +## try: +## echo client.postContent("http://validator.w3.org/check", multipart=data) +## finally: +## client.close() ## ## To stream files from disk when performing the request, use `addFiles`. ## @@ -66,8 +74,10 @@ ## var client = newHttpClient() ## var data = newMultipartData() ## data.addFiles({"uploaded_file": "test.html"}, mimeDb = mimes) -## -## echo client.postContent("http://validator.w3.org/check", multipart=data) +## try: +## echo client.postContent("http://validator.w3.org/check", multipart=data) +## finally: +## client.close() ## ## You can also make post requests with custom headers. ## This example sets `Content-Type` to `application/json` @@ -81,8 +91,11 @@ ## let body = %*{ ## "data": "some text" ## } -## let response = client.request("http://some.api", httpMethod = HttpPost, body = $body) -## echo response.status +## try: +## let response = client.request("http://some.api", httpMethod = HttpPost, body = $body) +## echo response.status +## finally: +## client.close() ## ## Progress reporting ## ================== @@ -101,7 +114,10 @@ ## proc asyncProc() {.async.} = ## var client = newAsyncHttpClient() ## client.onProgressChanged = onProgressChanged -## discard await client.getContent("http://speedtest-ams2.digitalocean.com/100mb.test") +## try: +## discard await client.getContent("http://speedtest-ams2.digitalocean.com/100mb.test") +## finally: +## client.close() ## ## waitFor asyncProc() ## @@ -221,6 +237,9 @@ import std/[ asyncnet, asyncdispatch, asyncfile, nativesockets, ] +when defined(nimPreviewSlimSystem): + import std/[assertions, syncio] + export httpcore except parseHeader # TODO: The `except` doesn't work type @@ -258,9 +277,9 @@ proc contentLength*(response: Response | AsyncResponse): int = ## This is effectively the value of the "Content-Length" header. ## ## A `ValueError` exception will be raised if the value is not an integer. - var contentLengthHeader = response.headers.getOrDefault("Content-Length") + ## If the Content-Length header is not set in the response, ContentLength is set to the value -1. + var contentLengthHeader = response.headers.getOrDefault("Content-Length", @["-1"]) result = contentLengthHeader.parseInt() - doAssert(result >= 0 and result <= high(int32)) proc lastModified*(response: Response | AsyncResponse): DateTime = ## Retrieves the specified response's last modified time. diff --git a/lib/pure/includes/osenv.nim b/lib/pure/includes/osenv.nim index 4a776eb78756..b7200a8e2a28 100644 --- a/lib/pure/includes/osenv.nim +++ b/lib/pure/includes/osenv.nim @@ -1,6 +1,6 @@ # Include file that implements 'getEnv' and friends. Do not import it! -when not declared(os) and not declared(ospaths): +when not declared(os): {.error: "This is an include file for os.nim!".} when not defined(nimscript): @@ -42,14 +42,18 @@ when not defined(nimscript): else: - proc c_getenv(env: cstring): cstring {. - importc: "getenv", header: "".} when defined(windows): proc c_putenv(envstring: cstring): cint {.importc: "_putenv", header: "".} from std/private/win_setenv import setEnvImpl + proc c_wgetenv(varname: WideCString): WideCString {.importc: "_wgetenv", + header: "".} + proc getEnvImpl(env: cstring): WideCString = c_wgetenv(env.newWideCString) else: + proc c_getenv(env: cstring): cstring {. + importc: "getenv", header: "".} proc c_setenv(envname: cstring, envval: cstring, overwrite: cint): cint {.importc: "setenv", header: "".} - proc c_unsetenv(env: cstring): cint {.importc: "unsetenv", header: "".} + proc c_unsetenv(env: cstring): cint {.importc: "unsetenv", header: "".} + proc getEnvImpl(env: cstring): cstring = c_getenv(env) proc getEnv*(key: string, default = ""): string {.tags: [ReadEnvEffect].} = ## Returns the value of the `environment variable`:idx: named `key`. @@ -67,7 +71,7 @@ when not defined(nimscript): assert getEnv("unknownEnv") == "" assert getEnv("unknownEnv", "doesn't exist") == "doesn't exist" - let env = c_getenv(key) + let env = getEnvImpl(key) if env == nil: return default result = $env @@ -83,7 +87,7 @@ when not defined(nimscript): runnableExamples: assert not existsEnv("unknownEnv") - return c_getenv(key) != nil + return getEnvImpl(key) != nil proc putEnv*(key, val: string) {.tags: [WriteEnvEffect].} = ## Sets the value of the `environment variable`:idx: named `key` to `val`. diff --git a/lib/pure/includes/oserr.nim b/lib/pure/includes/oserr.nim index 01403fa70d01..a6eba84ba10d 100644 --- a/lib/pure/includes/oserr.nim +++ b/lib/pure/includes/oserr.nim @@ -1,6 +1,6 @@ # Include file that implements 'osErrorMsg' and friends. Do not import it! -when not declared(os) and not declared(ospaths): +when not declared(os): {.error: "This is an include file for os.nim!".} when not defined(nimscript): diff --git a/lib/pure/ioselects/ioselectors_select.nim b/lib/pure/ioselects/ioselectors_select.nim index 4c38ba604e46..34c88d85e1fd 100644 --- a/lib/pure/ioselects/ioselectors_select.nim +++ b/lib/pure/ioselects/ioselectors_select.nim @@ -398,18 +398,6 @@ proc contains*[T](s: Selector[T], fd: SocketHandle|int): bool {.inline.} = if s.fds[i].ident == fdi: return true -when hasThreadSupport: - template withSelectLock[T](s: Selector[T], body: untyped) = - acquire(s.lock) - {.locks: [s.lock].}: - try: - body - finally: - release(s.lock) -else: - template withSelectLock[T](s: Selector[T], body: untyped) = - body - proc getData*[T](s: Selector[T], fd: SocketHandle|int): var T = s.withSelectLock(): let fdi = int(fd) diff --git a/lib/pure/json.nim b/lib/pure/json.nim index 8e4db93b090e..d0b1a4051b2d 100644 --- a/lib/pure/json.nim +++ b/lib/pure/json.nim @@ -165,7 +165,7 @@ import options # xxx remove this dependency using same approach as https://githu import std/private/since when defined(nimPreviewSlimSystem): - import std/[syncio, assertions] + import std/[syncio, assertions, formatfloat] export tables.`$` @@ -218,10 +218,6 @@ proc newJRawNumber(s: string): JsonNode = ## to the string representation without the quotes. result = JsonNode(kind: JString, str: s, isUnquoted: true) -proc newJStringMove(s: string): JsonNode = - result = JsonNode(kind: JString) - shallowCopy(result.str, s) - proc newJInt*(n: BiggestInt): JsonNode = ## Creates a new `JInt JsonNode`. result = JsonNode(kind: JInt, num: n) @@ -859,8 +855,12 @@ proc parseJson(p: var JsonParser; rawIntegers, rawFloats: bool, depth = 0): Json case p.tok of tkString: # we capture 'p.a' here, so we need to give it a fresh buffer afterwards: - result = newJStringMove(p.a) - p.a = "" + when defined(gcArc) or defined(gcOrc): + result = JsonNode(kind: JString, str: move p.a) + else: + result = JsonNode(kind: JString) + shallowCopy(result.str, p.a) + p.a = "" discard getTok(p) of tkInt: if rawIntegers: diff --git a/lib/pure/logging.nim b/lib/pure/logging.nim index 6751a372a854..cbe8a827aa64 100644 --- a/lib/pure/logging.nim +++ b/lib/pure/logging.nim @@ -367,7 +367,7 @@ method log*(logger: ConsoleLogger, level: Level, args: varargs[string, `$`]) = if level >= logging.level and level >= logger.levelThreshold: let ln = substituteLog(logger.fmtStr, level, args) when defined(js): - let cln: cstring = ln + let cln = ln.cstring case level of lvlDebug: {.emit: "console.debug(`cln`);".} of lvlInfo: {.emit: "console.info(`cln`);".} diff --git a/lib/pure/marshal.nim b/lib/pure/marshal.nim index 452af54d5d78..848d7e3fb391 100644 --- a/lib/pure/marshal.nim +++ b/lib/pure/marshal.nim @@ -56,6 +56,9 @@ Please contribute a new implementation.""".} import streams, typeinfo, json, intsets, tables, unicode +when defined(nimPreviewSlimSystem): + import std/[assertions, formatfloat] + proc ptrToInt(x: pointer): int {.inline.} = result = cast[int](x) # don't skip alignment @@ -163,7 +166,10 @@ proc loadAny(p: var JsonParser, a: Any, t: var Table[BiggestInt, pointer]) = of akSequence: case p.kind of jsonNull: - setPointer(a, nil) + when defined(nimSeqsV2): + invokeNewSeq(a, 0) + else: + setPointer(a, nil) next(p) of jsonArrayStart: next(p) @@ -230,7 +236,10 @@ proc loadAny(p: var JsonParser, a: Any, t: var Table[BiggestInt, pointer]) = of akString: case p.kind of jsonNull: - setPointer(a, nil) + when defined(nimSeqsV2): + setString(a, "") + else: + setPointer(a, nil) next(p) of jsonString: setString(a, p.str) @@ -282,7 +291,7 @@ proc load*[T](s: Stream, data: var T) = var tab = initTable[BiggestInt, pointer]() loadAny(s, toAny(data), tab) -proc store*[T](s: Stream, data: T) = +proc store*[T](s: Stream, data: sink T) = ## Stores `data` into the stream `s`. Raises `IOError` in case of an error. runnableExamples: import std/streams @@ -295,13 +304,16 @@ proc store*[T](s: Stream, data: T) = var stored = initIntSet() var d: T - shallowCopy(d, data) + when defined(gcArc) or defined(gcOrc): + d = data + else: + shallowCopy(d, data) storeAny(s, toAny(d), stored) proc loadVM[T](typ: typedesc[T], x: T): string = discard "the implementation is in the compiler/vmops" -proc `$$`*[T](x: T): string = +proc `$$`*[T](x: sink T): string = ## Returns a string representation of `x` (serialization, marshalling). ## ## **Note:** to serialize `x` to JSON use `%x` from the `json` module @@ -321,7 +333,10 @@ proc `$$`*[T](x: T): string = else: var stored = initIntSet() var d: T - shallowCopy(d, x) + when defined(gcArc) or defined(gcOrc): + d = x + else: + shallowCopy(d, x) var s = newStringStream() storeAny(s, toAny(d), stored) result = s.data diff --git a/lib/pure/memfiles.nim b/lib/pure/memfiles.nim index f65ca125e11f..a952933eda4c 100644 --- a/lib/pure/memfiles.nim +++ b/lib/pure/memfiles.nim @@ -24,6 +24,10 @@ else: import os, streams +when defined(nimPreviewSlimSystem): + import std/[syncio, assertions] + + proc newEIO(msg: string): ref IOError = new(result) result.msg = msg @@ -313,9 +317,9 @@ when defined(posix) or defined(nimdoc): raiseOSError(osLastError()) when defined(linux): #Maybe NetBSD, too? #On Linux this can be over 100 times faster than a munmap,mmap cycle. - proc mremap(old: pointer; oldSize, newSize: csize; flags: cint): + proc mremap(old: pointer; oldSize, newSize: csize_t; flags: cint): pointer {.importc: "mremap", header: "".} - let newAddr = mremap(f.mem, csize(f.size), csize(newFileSize), cint(1)) + let newAddr = mremap(f.mem, csize_t(f.size), csize_t(newFileSize), cint(1)) if newAddr == cast[pointer](MAP_FAILED): raiseOSError(osLastError()) else: @@ -377,7 +381,7 @@ proc `$`*(ms: MemSlice): string {.inline.} = copyMem(addr(result[0]), ms.data, ms.size) iterator memSlices*(mfile: MemFile, delim = '\l', eat = '\r'): MemSlice {.inline.} = - ## Iterates over [optional `eat`] `delim`-delimited slices in MemFile `mfile`. + ## Iterates over \[optional `eat`] `delim`-delimited slices in MemFile `mfile`. ## ## Default parameters parse lines ending in either Unix(\\l) or Windows(\\r\\l) ## style on on a line-by-line basis. I.e., not every line needs the same ending. @@ -408,7 +412,7 @@ iterator memSlices*(mfile: MemFile, delim = '\l', eat = '\r'): MemSlice {.inline ## inc(count) ## echo count - proc c_memchr(cstr: pointer, c: char, n: csize): pointer {. + proc c_memchr(cstr: pointer, c: char, n: csize_t): pointer {. importc: "memchr", header: "".} proc `-!`(p, q: pointer): int {.inline.} = return cast[int](p) -% cast[int](q) var ms: MemSlice @@ -416,7 +420,7 @@ iterator memSlices*(mfile: MemFile, delim = '\l', eat = '\r'): MemSlice {.inline ms.data = mfile.mem var remaining = mfile.size while remaining > 0: - ending = c_memchr(ms.data, delim, remaining) + ending = c_memchr(ms.data, delim, csize_t(remaining)) if ending == nil: # unterminated final slice ms.size = remaining # Weird case..check eat? yield ms diff --git a/lib/pure/mimetypes.nim b/lib/pure/mimetypes.nim index d1566d897dfc..346fb39eeddc 100644 --- a/lib/pure/mimetypes.nim +++ b/lib/pure/mimetypes.nim @@ -29,6 +29,10 @@ runnableExamples: import tables from strutils import startsWith, toLowerAscii, strip +when defined(nimPreviewSlimSystem): + import std/assertions + + type MimeDB* = object mimes: OrderedTable[string, string] diff --git a/lib/pure/nativesockets.nim b/lib/pure/nativesockets.nim index 5453226a0ea1..2599a8acc4c9 100644 --- a/lib/pure/nativesockets.nim +++ b/lib/pure/nativesockets.nim @@ -16,6 +16,8 @@ import os, options import std/private/since import std/strbasics +when defined(nimPreviewSlimSystem): + import std/[assertions, syncio] when hostOS == "solaris": {.passl: "-lsocket -lnsl".} @@ -167,7 +169,7 @@ when not useWinVersion: else: proc toInt(domain: Domain): cint = - result = toU32(ord(domain)).cint + result = cast[cint](uint32(ord(domain))) proc toKnownDomain*(family: cint): Option[Domain] = ## Converts the platform-dependent `cint` to the Domain or none(), @@ -214,7 +216,7 @@ proc getProtoByName*(name: string): int {.since: (1, 3, 5).} = let protoent = posix.getprotobyname(name.cstring) if protoent == nil: - raise newException(OSError, "protocol not found") + raise newException(OSError, "protocol not found: " & name) result = protoent.p_proto.int @@ -300,7 +302,7 @@ proc getAddrInfo*(address: string, port: Port, domain: Domain = AF_INET, if domain == AF_INET6: hints.ai_flags = AI_V4MAPPED let socketPort = if sockType == SOCK_RAW: "" else: $port - var gaiResult = getaddrinfo(address, socketPort, addr(hints), result) + var gaiResult = getaddrinfo(address, socketPort.cstring, addr(hints), result) if gaiResult != 0'i32: when useWinVersion or defined(freertos): raiseOSError(osLastError()) @@ -373,9 +375,9 @@ when not useNimNetLite: ## ## On posix this will search through the `/etc/services` file. when useWinVersion: - var s = winlean.getservbyport(ze(int16(port)).cint, proto) + var s = winlean.getservbyport(uint16(port).cint, proto) else: - var s = posix.getservbyport(ze(int16(port)).cint, proto) + var s = posix.getservbyport(uint16(port).cint, proto) if s == nil: raiseOSError(osLastError(), "Service not found.") result.name = $s.s_name result.aliases = cstringArrayToSeq(s.s_aliases) @@ -460,10 +462,10 @@ when not useNimNetLite: const size = 256 result = newString(size) when useWinVersion: - let success = winlean.gethostname(result, size) + let success = winlean.gethostname(result.cstring, size) else: # Posix - let success = posix.gethostname(result, size) + let success = posix.gethostname(result.cstring, size) if success != 0.cint: raiseOSError(osLastError()) let x = len(cstring(result)) diff --git a/lib/pure/net.nim b/lib/pure/net.nim index 370b83e5435c..16390d9afdfe 100644 --- a/lib/pure/net.nim +++ b/lib/pure/net.nim @@ -90,6 +90,9 @@ runnableExamples("-r:off"): import std/private/since +when defined(nimPreviewSlimSystem): + import std/assertions + import nativesockets import os, strutils, times, sets, options, std/monotimes import ssl_config @@ -203,6 +206,30 @@ type when defined(nimHasStyleChecks): {.pop.} + +when defined(posix) and not defined(lwip): + from posix import TPollfd, POLLIN, POLLPRI, POLLOUT, POLLWRBAND, Tnfds + + template monitorPollEvent(x: var SocketHandle, y: cint, timeout: int): int = + var tpollfd: TPollfd + tpollfd.fd = cast[cint](x) + tpollfd.events = y + posix.poll(addr(tpollfd), Tnfds(1), timeout) + +proc timeoutRead(fd: var SocketHandle, timeout = 500): int = + when defined(windows) or defined(lwip): + var fds = @[fd] + selectRead(fds, timeout) + else: + monitorPollEvent(fd, POLLIN or POLLPRI, timeout) + +proc timeoutWrite(fd: var SocketHandle, timeout = 500): int = + when defined(windows) or defined(lwip): + var fds = @[fd] + selectWrite(fds, timeout) + else: + monitorPollEvent(fd, POLLOUT or POLLWRBAND, timeout) + proc socketError*(socket: Socket, err: int = -1, async = false, lastError = (-1).OSErrorCode, flags: set[SocketFlag] = {}) {.gcsafe.} @@ -519,18 +546,20 @@ proc fromSockAddr*(sa: Sockaddr_storage | SockAddr | Sockaddr_in | Sockaddr_in6, fromSockAddrAux(cast[ptr Sockaddr_storage](unsafeAddr sa), sl, address, port) when defineSsl: - CRYPTO_malloc_init() - doAssert SslLibraryInit() == 1 - SSL_load_error_strings() - ERR_load_BIO_strings() - OpenSSL_add_all_algorithms() + # OpenSSL >= 1.1.0 does not need explicit init. + when not useOpenssl3: + CRYPTO_malloc_init() + doAssert SslLibraryInit() == 1 + SSL_load_error_strings() + ERR_load_BIO_strings() + OpenSSL_add_all_algorithms() proc sslHandle*(self: Socket): SslPtr = ## Retrieve the ssl pointer of `socket`. ## Useful for interfacing with `openssl`. self.sslHandle - proc raiseSSLError*(s = "") = + proc raiseSSLError*(s = "") {.raises: [SslError].}= ## Raises a new SSL error. if s != "": raise newException(SslError, s) @@ -595,9 +624,7 @@ when defineSsl: caDir = "", caFile = ""): SslContext = ## Creates an SSL context. ## - ## Protocol version specifies the protocol to use. SSLv2, SSLv3, TLSv1 - ## are available with the addition of `protSSLv23` which allows for - ## compatibility with all of them. + ## protVersion is currently unsed. ## ## There are three options for verify mode: ## `CVerifyNone`: certificates are not verified; @@ -624,16 +651,12 @@ when defineSsl: ## or using ECDSA: ## - `openssl ecparam -out mykey.pem -name secp256k1 -genkey` ## - `openssl req -new -key mykey.pem -x509 -nodes -days 365 -out mycert.pem` - var newCTX: SslCtx - case protVersion - of protSSLv23: - newCTX = SSL_CTX_new(SSLv23_method()) # SSlv2,3 and TLS1 support. - of protSSLv2: - raiseSSLError("SSLv2 is no longer secure and has been deprecated, use protSSLv23") - of protSSLv3: - raiseSSLError("SSLv3 is no longer secure and has been deprecated, use protSSLv23") - of protTLSv1: - newCTX = SSL_CTX_new(TLSv1_method()) + let mtd = TLS_method() + if mtd == nil: + raiseSSLError("Failed to create TLS context") + var newCTX = SSL_CTX_new(mtd) + if newCTX == nil: + raiseSSLError("Failed to create TLS context") if newCTX.SSL_CTX_set_cipher_list(cipherList) != 1: raiseSSLError() @@ -682,7 +705,11 @@ when defineSsl: var found = false let useEnvVars = (if verifyMode == CVerifyPeerUseEnvVars: true else: false) for fn in scanSSLCertificates(useEnvVars = useEnvVars): - if newCTX.SSL_CTX_load_verify_locations(fn, nil) == VerifySuccess: + if fn.extractFilename == "": + if newCTX.SSL_CTX_load_verify_locations(nil, cstring(fn.normalizePathEnd(false))) == VerifySuccess: + found = true + break + elif newCTX.SSL_CTX_load_verify_locations(cstring(fn), nil) == VerifySuccess: found = true break if not found: @@ -788,24 +815,28 @@ when defineSsl: if SSL_set_fd(socket.sslHandle, socket.fd) != 1: raiseSSLError() - proc checkCertName(socket: Socket, hostname: string) = + proc checkCertName(socket: Socket, hostname: string) {.raises: [SslError], tags:[RootEffect].} = ## Check if the certificate Subject Alternative Name (SAN) or Subject CommonName (CN) matches hostname. ## Wildcards match only in the left-most label. ## When name starts with a dot it will be matched by a certificate valid for any subdomain when not defined(nimDisableCertificateValidation) and not defined(windows): assert socket.isSsl - let certificate = socket.sslHandle.SSL_get_peer_certificate() - if certificate.isNil: - raiseSSLError("No SSL certificate found.") - - const X509_CHECK_FLAG_ALWAYS_CHECK_SUBJECT = 0x1.cuint - # https://www.openssl.org/docs/man1.1.1/man3/X509_check_host.html - let match = certificate.X509_check_host(hostname.cstring, hostname.len.cint, - X509_CHECK_FLAG_ALWAYS_CHECK_SUBJECT, nil) - # https://www.openssl.org/docs/man1.1.1/man3/SSL_get_peer_certificate.html - X509_free(certificate) - if match != 1: - raiseSSLError("SSL Certificate check failed.") + try: + let certificate = socket.sslHandle.SSL_get_peer_certificate() + if certificate.isNil: + raiseSSLError("No SSL certificate found.") + + const X509_CHECK_FLAG_ALWAYS_CHECK_SUBJECT = 0x1.cuint + # https://www.openssl.org/docs/man1.1.1/man3/X509_check_host.html + let match = certificate.X509_check_host(hostname.cstring, hostname.len.cint, + X509_CHECK_FLAG_ALWAYS_CHECK_SUBJECT, nil) + # https://www.openssl.org/docs/man1.1.1/man3/SSL_get_peer_certificate.html + X509_free(certificate) + if match != 1: + raiseSSLError("SSL Certificate check failed.") + + except LibraryError: + raiseSSLError("SSL import failed") proc wrapConnectedSocket*(ctx: SslContext, socket: Socket, handshake: SslHandshakeType, @@ -832,6 +863,7 @@ when defineSsl: let ret = SSL_connect(socket.sslHandle) socketError(socket, ret) when not defined(nimDisableCertificateValidation) and not defined(windows): + # FIXME: this should be skipped on CVerifyNone if hostname.len > 0 and not isIpAddress(hostname): socket.checkCertName(hostname) of handshakeAsServer: @@ -1287,7 +1319,7 @@ when defined(nimdoc) or (defined(posix) and not useNimNetLite): (sizeof(socketAddr.sun_family) + path.len).SockLen) != 0'i32: raiseOSError(osLastError()) -when defined(ssl): +when defineSsl: proc gotHandshake*(socket: Socket): bool = ## Determines whether a handshake has occurred between a client (`socket`) ## and the server that `socket` is connected to. @@ -1308,14 +1340,6 @@ proc hasDataBuffered*(s: Socket): bool = if s.isSsl and not result: result = s.sslHasPeekChar -proc select(readfd: Socket, timeout = 500): int = - ## Used for socket operation timeouts. - if readfd.hasDataBuffered: - return 1 - - var fds = @[readfd.fd] - result = selectRead(fds, timeout) - proc isClosed(socket: Socket): bool = socket.fd == osInvalidSocket @@ -1429,7 +1453,9 @@ proc waitFor(socket: Socket, waited: var Duration, timeout, size: int, return min(sslPending, size) var startTime = getMonoTime() - let selRet = select(socket, (timeout - waited.inMilliseconds).int) + let selRet = if socket.hasDataBuffered: 1 + else: + timeoutRead(socket.fd, (timeout - waited.inMilliseconds).int) if selRet < 0: raiseOSError(osLastError()) if selRet != 1: raise newException(TimeoutError, "Call to '" & funcName & "' timed out.") @@ -1980,7 +2006,7 @@ proc dial*(address: string, port: Port, raise newException(IOError, "Couldn't resolve address: " & address) proc connect*(socket: Socket, address: string, - port = Port(0)) {.tags: [ReadIOEffect].} = + port = Port(0)) {.tags: [ReadIOEffect, RootEffect].} = ## Connects socket to `address`:`port`. `Address` can be an IP address or a ## host name. If `address` is a host name, this function will try each IP ## of that host name. `htons` is already performed on `port` so you must @@ -2055,7 +2081,7 @@ proc connectAsync(socket: Socket, name: string, port = Port(0), if not success: raiseOSError(lastError) proc connect*(socket: Socket, address: string, port = Port(0), - timeout: int) {.tags: [ReadIOEffect, WriteIOEffect].} = + timeout: int) {.tags: [ReadIOEffect, WriteIOEffect, RootEffect].} = ## Connects to server as specified by `address` on port specified by `port`. ## ## The `timeout` parameter specifies the time in milliseconds to allow for @@ -2063,8 +2089,7 @@ proc connect*(socket: Socket, address: string, port = Port(0), socket.fd.setBlocking(false) socket.connectAsync(address, port, socket.domain) - var s = @[socket.fd] - if selectWrite(s, timeout) != 1: + if timeoutWrite(socket.fd, timeout) != 1: raise newException(TimeoutError, "Call to 'connect' timed out.") else: let res = getSockOptInt(socket.fd, SOL_SOCKET, SO_ERROR) diff --git a/lib/pure/oids.nim b/lib/pure/oids.nim index 776c046b1556..43eadad27b25 100644 --- a/lib/pure/oids.nim +++ b/lib/pure/oids.nim @@ -9,8 +9,7 @@ ## Nim OID support. An OID is a global ID that consists of a timestamp, ## a unique counter and a random value. This combination should suffice to -## produce a globally distributed unique ID. This implementation was extracted -## from the MongoDB interface and is thus binary compatible with a MongoDB OID. +## produce a globally distributed unique ID. ## ## This implementation calls `initRand()` for the first call of ## `genOid`. @@ -20,7 +19,7 @@ from std/private/decode_helpers import handleHexChar type Oid* = object ## An OID. - time: int32 + time: int64 fuzz: int32 count: int32 @@ -44,37 +43,27 @@ proc parseOid*(str: cstring): Oid = ## Parses an OID. var bytes = cast[cstring](addr(result.time)) var i = 0 - while i < 12: + while i < 16: bytes[i] = chr((hexbyte(str[2 * i]) shl 4) or hexbyte(str[2 * i + 1])) inc(i) -template toStringImpl[T: string | cstring](result: var T, oid: Oid) = - ## Stringifies `oid`. +proc `$`*(oid: Oid): string = + ## Converts an OID to a string. const hex = "0123456789abcdef" - const N = 24 - when T is string: - result.setLen N + result.setLen 32 var o = oid var bytes = cast[cstring](addr(o)) var i = 0 - while i < 12: + while i < 16: let b = bytes[i].ord result[2 * i] = hex[(b and 0xF0) shr 4] result[2 * i + 1] = hex[b and 0xF] inc(i) - when T is cstring: - result[N] = '\0' - - -proc `$`*(oid: Oid): string = - ## Converts an OID to a string. - toStringImpl(result, oid) - let - t = getTime().toUnix.int32 + t = getTime().toUnix var seed = initRand(t) @@ -84,24 +73,24 @@ let fuzz = cast[int32](seed.rand(high(int))) template genOid(result: var Oid, incr: var int, fuzz: int32) = - var time = getTime().toUnix.int32 + var time = getTime().toUnix var i = cast[int32](atomicInc(incr)) - bigEndian32(addr result.time, addr(time)) + bigEndian64(addr result.time, addr(time)) result.fuzz = fuzz bigEndian32(addr result.count, addr(i)) proc genOid*(): Oid = ## Generates a new OID. runnableExamples: - doAssert ($genOid()).len == 24 + doAssert ($genOid()).len == 32 runnableExamples("-r:off"): - echo $genOid() # for example, "5fc7f546ddbbc84800006aaf" + echo $genOid() # for example, "00000000632c452db08c3d19ee9073e5" genOid(result, incr, fuzz) proc generatedTime*(oid: Oid): Time = ## Returns the generated timestamp of the OID. - var tmp: int32 + var tmp: int64 var dummy = oid.time - bigEndian32(addr(tmp), addr(dummy)) + bigEndian64(addr(tmp), addr(dummy)) result = fromUnix(tmp) diff --git a/lib/pure/options.nim b/lib/pure/options.nim index 562ed6361b77..9dc4e096b33b 100644 --- a/lib/pure/options.nim +++ b/lib/pure/options.nim @@ -68,6 +68,10 @@ supports pattern matching on `Option`s, with the `Some()` and ]## # xxx pending https://github.com/timotheecour/Nim/issues/376 use `runnableExamples` and `whichModule` +when defined(nimHasEffectsOf): + {.experimental: "strictEffects".} +else: + {.pragma: effectsOf.} import typetraits @@ -226,7 +230,7 @@ proc get*[T](self: var Option[T]): var T {.inline.} = raise newException(UnpackDefect, "Can't obtain a value from a `none`") return self.val -proc map*[T](self: Option[T], callback: proc (input: T)) {.inline.} = +proc map*[T](self: Option[T], callback: proc (input: T)) {.inline, effectsOf: callback.} = ## Applies a `callback` function to the value of the `Option`, if it has one. ## ## **See also:** @@ -245,7 +249,7 @@ proc map*[T](self: Option[T], callback: proc (input: T)) {.inline.} = if self.isSome: callback(self.val) -proc map*[T, R](self: Option[T], callback: proc (input: T): R): Option[R] {.inline.} = +proc map*[T, R](self: Option[T], callback: proc (input: T): R): Option[R] {.inline, effectsOf: callback.} = ## Applies a `callback` function to the value of the `Option` and returns an ## `Option` containing the new value. ## @@ -282,7 +286,7 @@ proc flatten*[T](self: Option[Option[T]]): Option[T] {.inline.} = none(T) proc flatMap*[T, R](self: Option[T], - callback: proc (input: T): Option[R]): Option[R] {.inline.} = + callback: proc (input: T): Option[R]): Option[R] {.inline, effectsOf: callback.} = ## Applies a `callback` function to the value of the `Option` and returns the new value. ## ## If the `Option` has no value, `none(R)` will be returned. @@ -307,7 +311,7 @@ proc flatMap*[T, R](self: Option[T], map(self, callback).flatten() -proc filter*[T](self: Option[T], callback: proc (input: T): bool): Option[T] {.inline.} = +proc filter*[T](self: Option[T], callback: proc (input: T): bool): Option[T] {.inline, effectsOf: callback.} = ## Applies a `callback` to the value of the `Option`. ## ## If the `callback` returns `true`, the option is returned as `some`. diff --git a/lib/pure/os.nim b/lib/pure/os.nim index fa379a228056..b240a23ff4b8 100644 --- a/lib/pure/os.nim +++ b/lib/pure/os.nim @@ -236,6 +236,9 @@ proc `/`*(head, tail: string): string {.noSideEffect, inline.} = result = joinPath(head, tail) +when doslikeFileSystem: + import std/private/ntpath + proc splitPath*(path: string): tuple[head, tail: string] {. noSideEffect, rtl, extern: "nos$1".} = ## Splits a directory into `(head, tail)` tuple, so that @@ -258,8 +261,14 @@ proc splitPath*(path: string): tuple[head, tail: string] {. assert splitPath("bin") == ("", "bin") assert splitPath("") == ("", "") + when doslikeFileSystem: + let (drive, splitpath) = splitDrive(path) + let stop = drive.len + else: + const stop = 0 + var sepPos = -1 - for i in countdown(len(path)-1, 0): + for i in countdown(len(path)-1, stop): if path[i] in {DirSep, AltSep}: sepPos = i break @@ -272,8 +281,12 @@ proc splitPath*(path: string): tuple[head, tail: string] {. ) result.tail = substr(path, sepPos+1) else: - result.head = "" - result.tail = path + when doslikeFileSystem: + result.head = drive + result.tail = splitpath + else: + result.head = "" + result.tail = path proc isAbsolute*(path: string): bool {.rtl, noSideEffect, extern: "nos$1", raises: [].} = ## Checks whether a given `path` is absolute. @@ -318,9 +331,6 @@ when doslikeFileSystem: (path[0] == DirSep and (path.len == 1 or path[1] notin {DirSep, AltSep, ':'}))) - proc isUNCPrefix(path: string): bool {.noSideEffect, raises: [].} = - path[0] == DirSep and path[1] == DirSep - proc sameRoot(path1, path2: string): bool {.noSideEffect, raises: [].} = ## Return true if path1 and path2 have a same root. ## @@ -330,40 +340,12 @@ when doslikeFileSystem: assert(isAbsolute(path1)) assert(isAbsolute(path2)) - let - len1 = path1.len - len2 = path2.len - assert(len1 != 0 and len2 != 0) - if isAbsFromCurrentDrive(path1) and isAbsFromCurrentDrive(path2): - return true - elif len1 == 1 or len2 == 1: - return false + result = true + elif cmpIgnoreCase(splitDrive(path1).drive, splitDrive(path2).drive) == 0: + result = true else: - if path1[1] == ':' and path2[1] == ':': - return path1[0].toLowerAscii() == path2[0].toLowerAscii() - else: - var - p1, p2: PathIter - pp1 = next(p1, path1) - pp2 = next(p2, path2) - if pp1[1] - pp1[0] == 1 and pp2[1] - pp2[0] == 1 and - isUNCPrefix(path1) and isUNCPrefix(path2): - #UNC - var h = 0 - while p1.hasNext(path1) and p2.hasNext(path2) and h < 2: - pp1 = next(p1, path1) - pp2 = next(p2, path2) - let diff = pp1[1] - pp1[0] - if diff != pp2[1] - pp2[0]: - return false - for i in 0..diff: - if path1[i + pp1[0]] !=? path2[i + pp2[0]]: - return false - inc h - return h == 2 - else: - return false + result = false proc relativePath*(path, base: string, sep = DirSep): string {. rtl, extern: "nos$1".} = @@ -384,7 +366,8 @@ proc relativePath*(path, base: string, sep = DirSep): string {. runnableExamples: assert relativePath("/Users/me/bar/z.nim", "/Users/other/bad", '/') == "../../me/bar/z.nim" assert relativePath("/Users/me/bar/z.nim", "/Users/other", '/') == "../me/bar/z.nim" - assert relativePath("/Users///me/bar//z.nim", "//Users/", '/') == "me/bar/z.nim" + when not doslikeFileSystem: # On Windows, UNC-paths start with `//` + assert relativePath("/Users///me/bar//z.nim", "//Users/", '/') == "me/bar/z.nim" assert relativePath("/Users/me/bar/z.nim", "/Users/me", '/') == "bar/z.nim" assert relativePath("", "/users/moo", '/') == "" assert relativePath("foo", ".", '/') == "foo" @@ -497,6 +480,9 @@ proc parentDir*(path: string): string {. assert parentDir("a//./") == "." assert parentDir("a/b/c/..") == "a" result = pathnorm.normalizePath(path) + when doslikeFileSystem: + let (drive, splitpath) = splitDrive(result) + result = splitpath var sepPos = parentDirPos(result) if sepPos >= 0: result = substr(result, 0, sepPos) @@ -507,6 +493,13 @@ proc parentDir*(path: string): string {. result = "" else: result = "." + when doslikeFileSystem: + if result.len == 0: + discard + elif drive.len > 0 and result.len == 1 and result[0] in {DirSep, AltSep}: + result = drive + else: + result = drive & result proc tailDir*(path: string): string {. noSideEffect, rtl, extern: "nos$1".} = @@ -526,6 +519,10 @@ proc tailDir*(path: string): string {. assert tailDir("usr/local/bin") == "local/bin" var i = 0 + when doslikeFileSystem: + let (drive, splitpath) = path.splitDrive + if drive != "": + return splitpath.strip(chars = {DirSep, AltSep}, trailing = false) while i < len(path): if path[i] in {DirSep, AltSep}: while i < len(path) and path[i] in {DirSep, AltSep}: inc i @@ -544,6 +541,9 @@ proc isRootDir*(path: string): bool {. assert not isRootDir("/a") assert not isRootDir("a/b/c") + when doslikeFileSystem: + if splitDrive(path).path == "": + return true result = parentDirPos(path) < 0 iterator parentDirs*(path: string, fromRoot=false, inclusive=true): string = @@ -588,7 +588,11 @@ iterator parentDirs*(path: string, fromRoot=false, inclusive=true): string = current = current.parentDir yield current else: - for i in countup(0, path.len - 2): # ignore the last / + when doslikeFileSystem: + let start = path.splitDrive.drive.len + else: + const start = 0 + for i in countup(start, path.len - 2): # ignore the last / # deal with non-normalized paths such as /foo//bar//baz if path[i] in {DirSep, AltSep} and (i == 0 or path[i-1] notin {DirSep, AltSep}): @@ -608,11 +612,15 @@ proc `/../`*(head, tail: string): string {.noSideEffect.} = assert "a/b/c" /../ "d/e" == "a/b/d/e" assert "a" /../ "d/e" == "a/d/e" + when doslikeFileSystem: + let (drive, head) = splitDrive(head) let sepPos = parentDirPos(head) if sepPos >= 0: result = substr(head, 0, sepPos-1) / tail else: result = head / tail + when doslikeFileSystem: + result = drive / result proc normExt(ext: string): string = if ext == "" or ext[0] == ExtSep: result = ext # no copy needed here @@ -680,7 +688,13 @@ proc splitFile*(path: string): tuple[dir, name, ext: string] {. var namePos = 0 var dotPos = 0 - for i in countdown(len(path) - 1, 0): + when doslikeFileSystem: + let (drive, _) = splitDrive(path) + let stop = len(drive) + result.dir = drive + else: + const stop = 0 + for i in countdown(len(path) - 1, stop): if path[i] in {DirSep, AltSep} or i == 0: if path[i] in {DirSep, AltSep}: result.dir = substr(path, 0, if likely(i >= 1): i - 1 else: 0) @@ -1259,12 +1273,12 @@ proc findExe*(exe: string, followSymlinks: bool = true; while followSymlinks: # doubles as if here if x.symlinkExists: var r = newString(maxSymlinkLen) - var len = readlink(x, r, maxSymlinkLen) + var len = readlink(x.cstring, r.cstring, maxSymlinkLen) if len < 0: raiseOSError(osLastError(), exe) if len > maxSymlinkLen: r = newString(len+1) - len = readlink(x, r, len) + len = readlink(x.cstring, r.cstring, len) setLen(r, len) if isAbsolute(r): x = r @@ -1404,8 +1418,8 @@ when not defined(nimscript): var bufsize = 1024 # should be enough result = newString(bufsize) while true: - if getcwd(result, bufsize) != nil: - setLen(result, c_strlen(result)) + if getcwd(result.cstring, bufsize) != nil: + setLen(result, c_strlen(result.cstring)) break else: let err = osLastError() @@ -1741,7 +1755,7 @@ proc createSymlink*(src, dest: string) {.noWeirdTarget.} = ## by `src`. On most operating systems, will fail if a link already exists. ## ## .. warning:: Some OS's (such as Microsoft Windows) restrict the creation - ## of symlinks to root users (administrators) or users with developper mode enabled. + ## of symlinks to root users (administrators) or users with developer mode enabled. ## ## See also: ## * `createHardlink proc`_ @@ -1774,12 +1788,12 @@ proc expandSymlink*(symlinkPath: string): string {.noWeirdTarget.} = result = symlinkPath else: result = newString(maxSymlinkLen) - var len = readlink(symlinkPath, result, maxSymlinkLen) + var len = readlink(symlinkPath, result.cstring, maxSymlinkLen) if len < 0: raiseOSError(osLastError(), symlinkPath) if len > maxSymlinkLen: result = newString(len+1) - len = readlink(symlinkPath, result, len) + len = readlink(symlinkPath, result.cstring, len) setLen(result, len) const hasCCopyfile = defined(osx) and not defined(nimLegacyCopyFile) @@ -2344,7 +2358,7 @@ iterator walkDir*(dir: string; relative = false, checkDir = false): var k = pcFile template kSetGeneric() = # pure Posix component `k` resolution - if lstat(path, s) < 0'i32: continue # don't yield + if lstat(path.cstring, s) < 0'i32: continue # don't yield elif S_ISDIR(s.st_mode): k = pcDir elif S_ISLNK(s.st_mode): @@ -2383,21 +2397,21 @@ iterator walkDirRec*(dir: string, ## ## Walking is recursive. `followFilter` controls the behaviour of the iterator: ## - ## --------------------- --------------------------------------------- + ## ===================== ============================================= ## yieldFilter meaning - ## --------------------- --------------------------------------------- + ## ===================== ============================================= ## ``pcFile`` yield real files (default) ## ``pcLinkToFile`` yield symbolic links to files ## ``pcDir`` yield real directories ## ``pcLinkToDir`` yield symbolic links to directories - ## --------------------- --------------------------------------------- + ## ===================== ============================================= ## - ## --------------------- --------------------------------------------- + ## ===================== ============================================= ## followFilter meaning - ## --------------------- --------------------------------------------- + ## ===================== ============================================= ## ``pcDir`` follow real directories (default) ## ``pcLinkToDir`` follow symbolic links to directories - ## --------------------- --------------------------------------------- + ## ===================== ============================================= ## ## ## See also: @@ -2539,20 +2553,14 @@ proc createDir*(dir: string) {.rtl, extern: "nos$1", ## * `copyDir proc`_ ## * `copyDirWithPermissions proc`_ ## * `moveDir proc`_ - var omitNext = false - when doslikeFileSystem: - omitNext = isAbsolute(dir) - for i in 1.. dir.len-1: - if dir[i] in {DirSep, AltSep}: - if omitNext: - omitNext = false - else: - discard existsOrCreateDir(substr(dir, 0, i-1)) - - # The loop does not create the dir itself if it doesn't end in separator - if dir.len > 0 and not omitNext and - dir[^1] notin {DirSep, AltSep}: - discard existsOrCreateDir(dir) + if dir == "": + return + var omitNext = isAbsolute(dir) + for p in parentDirs(dir, fromRoot=true): + if omitNext: + omitNext = false + else: + discard existsOrCreateDir(p) proc copyDir*(source, dest: string) {.rtl, extern: "nos$1", tags: [ReadDirEffect, WriteIOEffect, ReadIOEffect], benign, noWeirdTarget.} = @@ -2939,7 +2947,7 @@ elif defined(genode): proc paramCount*(): int = raise newException(OSError, "paramCount is not implemented on Genode") -elif weirdTarget: +elif weirdTarget or (defined(posix) and appType == "lib"): proc paramStr*(i: int): string {.tags: [ReadIOEffect].} = raise newException(OSError, "paramStr is not implemented on current platform") @@ -3041,10 +3049,10 @@ when not weirdTarget and (defined(freebsd) or defined(dragonfly) or defined(netb when not weirdTarget and (defined(linux) or defined(solaris) or defined(bsd) or defined(aix)): proc getApplAux(procPath: string): string = result = newString(maxSymlinkLen) - var len = readlink(procPath, result, maxSymlinkLen) + var len = readlink(procPath, result.cstring, maxSymlinkLen) if len > maxSymlinkLen: result = newString(len+1) - len = readlink(procPath, result, len) + len = readlink(procPath, result.cstring, len) setLen(result, len) when not weirdTarget and defined(openbsd): @@ -3113,7 +3121,7 @@ when defined(haiku): B_FIND_PATH_IMAGE_PATH = 1000 proc find_path(codePointer: pointer, baseDirectory: cint, subPath: cstring, - pathBuffer: cstring, bufferSize: csize): int32 + pathBuffer: cstring, bufferSize: csize_t): int32 {.importc, header: "".} proc getApplHaiku(): string = @@ -3169,7 +3177,7 @@ proc getAppFilename*(): string {.rtl, extern: "nos$1", tags: [ReadIOEffect], noW var size = cuint32(0) getExecPath1(nil, size) result = newString(int(size)) - if getExecPath2(result, size): + if getExecPath2(result.cstring, size): result = "" # error! if result.len > 0: result = result.expandFilename diff --git a/lib/pure/osproc.nim b/lib/pure/osproc.nim index 512db92a4b3d..a0079cf95323 100644 --- a/lib/pure/osproc.nim +++ b/lib/pure/osproc.nim @@ -1086,9 +1086,9 @@ elif not defined(useNimRtl): var pid: Pid if (poUsePath in data.options): - res = posix_spawnp(pid, data.sysCommand, fops, attr, data.sysArgs, data.sysEnv) + res = posix_spawnp(pid, data.sysCommand.cstring, fops, attr, data.sysArgs, data.sysEnv) else: - res = posix_spawn(pid, data.sysCommand, fops, attr, data.sysArgs, data.sysEnv) + res = posix_spawn(pid, data.sysCommand.cstring, fops, attr, data.sysArgs, data.sysEnv) discard posix_spawn_file_actions_destroy(fops) discard posix_spawnattr_destroy(attr) @@ -1174,14 +1174,14 @@ elif not defined(useNimRtl): when defined(uClibc) or defined(linux) or defined(haiku): # uClibc environment (OpenWrt included) doesn't have the full execvpe let exe = findExe(data.sysCommand) - discard execve(exe, data.sysArgs, data.sysEnv) + discard execve(exe.cstring, data.sysArgs, data.sysEnv) else: # MacOSX doesn't have execvpe, so we need workaround. # On MacOSX we can arrive here only from fork, so this is safe: environ = data.sysEnv - discard execvp(data.sysCommand, data.sysArgs) + discard execvp(data.sysCommand.cstring, data.sysArgs) else: - discard execve(data.sysCommand, data.sysArgs, data.sysEnv) + discard execve(data.sysCommand.cstring, data.sysArgs, data.sysEnv) startProcessFail(data) {.pop.} @@ -1523,7 +1523,7 @@ elif not defined(useNimRtl): header: "".} proc execCmd(command: string): int = - when defined(linux): + when defined(posix): let tmp = csystem(command) result = if tmp == -1: tmp else: exitStatusLikeShell(tmp) else: diff --git a/lib/pure/parsejson.nim b/lib/pure/parsejson.nim index c92eac26e923..fcbcf8e364fa 100644 --- a/lib/pure/parsejson.nim +++ b/lib/pure/parsejson.nim @@ -221,7 +221,7 @@ proc parseString(my: var JsonParser): TokKind = add(my.a, 'u') inc(pos, 2) var pos2 = pos - var r = parseEscapedUTF16(my.buf, pos) + var r = parseEscapedUTF16(cstring(my.buf), pos) if r < 0: my.err = errInvalidToken break @@ -231,7 +231,7 @@ proc parseString(my: var JsonParser): TokKind = my.err = errInvalidToken break inc(pos, 2) - var s = parseEscapedUTF16(my.buf, pos) + var s = parseEscapedUTF16(cstring(my.buf), pos) if (s and 0xfc00) == 0xdc00 and s > 0: r = 0x10000 + (((r - 0xd800) shl 10) or (s - 0xdc00)) else: diff --git a/lib/pure/parseopt.nim b/lib/pure/parseopt.nim index fef354733d23..f8d03e0924a9 100644 --- a/lib/pure/parseopt.nim +++ b/lib/pure/parseopt.nim @@ -192,6 +192,10 @@ proc parseWord(s: string, i: int, w: var string, add(w, s[result]) inc(result) +proc initOptParser*(cmdline: seq[string], shortNoVal: set[char] = {}, + longNoVal: seq[string] = @[]; + allowWhitespaceAfterColon = true): OptParser + proc initOptParser*(cmdline = "", shortNoVal: set[char] = {}, longNoVal: seq[string] = @[]; allowWhitespaceAfterColon = true): OptParser = @@ -214,28 +218,7 @@ proc initOptParser*(cmdline = "", shortNoVal: set[char] = {}, p = initOptParser("--left --debug:3 -l -r:2", shortNoVal = {'l'}, longNoVal = @["left"]) - result.pos = 0 - result.idx = 0 - result.inShortState = false - result.shortNoVal = shortNoVal - result.longNoVal = longNoVal - result.allowWhitespaceAfterColon = allowWhitespaceAfterColon - if cmdline != "": - result.cmds = parseCmdLine(cmdline) - else: - when declared(paramCount): - result.cmds = newSeq[string](paramCount()) - for i in countup(1, paramCount()): - result.cmds[i-1] = paramStr(i) - else: - # we cannot provide this for NimRtl creation on Posix, because we can't - # access the command line arguments then! - doAssert false, "empty command line given but" & - " real command line is not accessible" - - result.kind = cmdEnd - result.key = "" - result.val = "" + initOptParser(parseCmdLine(cmdline), shortNoVal, longNoVal, allowWhitespaceAfterColon) proc initOptParser*(cmdline: seq[string], shortNoVal: set[char] = {}, longNoVal: seq[string] = @[]; diff --git a/lib/pure/parsesql.nim b/lib/pure/parsesql.nim index 61196aead664..3af840e29139 100644 --- a/lib/pure/parsesql.nim +++ b/lib/pure/parsesql.nim @@ -15,6 +15,9 @@ import strutils, lexbase import std/private/decode_helpers +when defined(nimPreviewSlimSystem): + import std/assertions + # ------------------- scanner ------------------------------------------------- type diff --git a/lib/pure/parsexml.nim b/lib/pure/parsexml.nim index 79a9cc7307dd..3ba5a19d5b5f 100644 --- a/lib/pure/parsexml.nim +++ b/lib/pure/parsexml.nim @@ -149,6 +149,9 @@ an HTML document contains. import strutils, lexbase, streams, unicode +when defined(nimPreviewSlimSystem): + import std/assertions + # the parser treats ``
    `` as ``

    `` # xmlElementCloseEnd, ## ``/>`` diff --git a/lib/pure/pathnorm.nim b/lib/pure/pathnorm.nim index 10a2a0b679ce..a71ae0762ecc 100644 --- a/lib/pure/pathnorm.nim +++ b/lib/pure/pathnorm.nim @@ -29,10 +29,6 @@ proc next*(it: var PathIter; x: string): (int, int) = if not it.notFirst and x[it.i] in {DirSep, AltSep}: # absolute path: inc it.i - when doslikeFileSystem: # UNC paths have leading `\\` - if hasNext(it, x) and x[it.i] == DirSep and - it.i+1 < x.len and x[it.i+1] != DirSep: - inc it.i else: while it.i < x.len and x[it.i] notin {DirSep, AltSep}: inc it.i if it.i > it.prev: @@ -56,10 +52,23 @@ 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} +when doslikeFileSystem: + import std/private/ntpath + proc addNormalizePath*(x: string; result: var string; state: var int; dirSep = DirSep) = ## Low level proc. Undocumented. + when doslikeFileSystem: # Add Windows drive at start without normalization + var x = x + if result == "": + let (drive, file) = splitDrive(x) + x = file + result.add drive + for c in result.mitems: + if c in {DirSep, AltSep}: + c = dirSep + # state: 0th bit set if isAbsolute path. Other bits count # the number of path components. var it: PathIter diff --git a/lib/pure/pegs.nim b/lib/pure/pegs.nim index 78f03d308b3a..a95700825545 100644 --- a/lib/pure/pegs.nim +++ b/lib/pure/pegs.nim @@ -17,7 +17,7 @@ include "system/inclrtl" when defined(nimPreviewSlimSystem): - import std/syncio + import std/[syncio, assertions] const useUnicode = true ## change this to deactivate proper UTF-8 support @@ -838,10 +838,13 @@ template matchOrParse(mopProc: untyped) = var idx = c.ml # reserve a slot for the subpattern result = mopProc(s, p.sons[0], start, c) if result >= 0: - inc(c.ml) if idx < MaxSubpatterns: + if idx != c.ml: + for i in countdown(c.ml, idx): + c.matches[i+1] = c.matches[i] c.matches[idx] = (start, start+result-1) #else: silently ignore the capture + inc(c.ml) leave(pkCapture, s, p, start, result) of pkBackRef: enter(pkBackRef, s, p, start) diff --git a/lib/pure/random.nim b/lib/pure/random.nim index b8aeb86e0030..de93f468ffdb 100644 --- a/lib/pure/random.nim +++ b/lib/pure/random.nim @@ -75,6 +75,9 @@ runnableExamples: import algorithm, math import std/private/since +when defined(nimPreviewSlimSystem): + import std/assertions + include system/inclrtl {.push debugger: off.} @@ -178,24 +181,33 @@ proc skipRandomNumbers*(s: var Rand) = ## **See also:** ## * `next proc<#next,Rand>`_ runnableExamples("--threads:on"): - import std/[random, threadpool] + import std/random - const spawns = 4 const numbers = 100000 - proc randomSum(r: Rand): int = - var r = r + var + thr: array[0..3, Thread[(Rand, int)]] + vals: array[0..3, int] + + proc randomSum(params: tuple[r: Rand, index: int]) {.thread.} = + var r = params.r + var s = 0 # avoid cache thrashing for i in 1..numbers: - result += r.rand(0..10) + s += r.rand(0..10) + vals[params.index] = s var r = initRand(2019) - var vals: array[spawns, FlowVar[int]] - for val in vals.mitems: - val = spawn randomSum(r) + for i in 0..`_ has not been called, the sequence of random + ## numbers returned from this proc will always be the same. + ## + ## **See also:** + ## * `rand proc<#rand,int>`_ that returns an integer + ## * `rand proc<#rand,float>`_ that returns a floating point number + ## * `rand proc<#rand,HSlice[T: Ordinal or float or float32 or float64,T: Ordinal or float or float32 or float64]>`_ + ## that accepts a slice + when T is range or T is enum: + result = rand(r, low(T)..high(T)) + elif T is bool: + when defined(js): + result = (r.next or 0) < 0 + else: + result = cast[int64](r.next) < 0 + else: + when defined(js): + result = cast[T](r.next shr (sizeof(uint)*8 - sizeof(T)*8)) + else: + result = cast[T](r.next shr (sizeof(uint64)*8 - sizeof(T)*8)) + +proc rand*[T: Ordinal](t: typedesc[T]): T = + ## Returns a random Ordinal in the range `low(T)..high(T)`. ## ## If `randomize <#randomize>`_ has not been called, the sequence of random ## numbers returned from this proc will always be the same. @@ -368,16 +404,15 @@ proc rand*[T: SomeInteger](t: typedesc[T]): T = ## that accepts a slice runnableExamples: randomize(567) - if false: # implementation defined - assert rand(int8) == -42 - assert rand(uint32) == 578980729'u32 - assert rand(range[1..16]) == 11 - # pending csources >= 1.4.0 or fixing https://github.com/timotheecour/Nim/issues/251#issuecomment-831599772, - # use `runnableExamples("-r:off")` instead of `if false` - when T is range: - result = rand(state, low(T)..high(T)) - else: - result = cast[T](state.next) + type E = enum a, b, c, d + + assert rand(E) in a..d + assert rand(char) in low(char)..high(char) + assert rand(int8) in low(int8)..high(int8) + assert rand(uint32) in low(uint32)..high(uint32) + assert rand(range[1..16]) in 1..16 + + result = rand(state, t) proc sample*[T](r: var Rand; s: set[T]): T = ## Returns a random element from the set `s` using the given state. diff --git a/lib/pure/rationals.nim b/lib/pure/rationals.nim index 800979cda3e1..aba0f4ce4bbf 100644 --- a/lib/pure/rationals.nim +++ b/lib/pure/rationals.nim @@ -22,6 +22,8 @@ runnableExamples: doAssert r1 / r2 == -2 // 3 import math, hashes +when defined(nimPreviewSlimSystem): + import std/assertions type Rational*[T] = object ## A rational number, consisting of a numerator `num` and a denominator `den`. diff --git a/lib/pure/ropes.nim b/lib/pure/ropes.nim index 84b5b47c2f8c..b973fd2226c4 100644 --- a/lib/pure/ropes.nim +++ b/lib/pure/ropes.nim @@ -20,7 +20,7 @@ include system/inclrtl import streams when defined(nimPreviewSlimSystem): - import std/syncio + import std/[syncio, formatfloat, assertions] {.push debugger: off.} # the user does not want to trace a part # of the standard library! diff --git a/lib/pure/selectors.nim b/lib/pure/selectors.nim index 313702370141..ab7e104fcd9e 100644 --- a/lib/pure/selectors.nim +++ b/lib/pure/selectors.nim @@ -29,6 +29,9 @@ import os, nativesockets +when defined(nimPreviewSlimSystem): + import std/assertions + const hasThreadSupport = compileOption("threads") and defined(threadsafe) const ioselSupportedPlatform* = defined(macosx) or defined(freebsd) or diff --git a/lib/pure/ssl_certs.nim b/lib/pure/ssl_certs.nim index c7ce04ffae1c..dd09848beb3b 100644 --- a/lib/pure/ssl_certs.nim +++ b/lib/pure/ssl_certs.nim @@ -88,7 +88,7 @@ when defined(haiku): proc find_paths_etc(architecture: cstring, baseDirectory: cint, subPath: cstring, flags: uint32, paths: var ptr UncheckedArray[cstring], - pathCount: var csize): int32 + pathCount: var csize_t): int32 {.importc, header: "".} proc free(p: pointer) {.importc, header: "".} @@ -126,12 +126,18 @@ iterator scanSSLCertificates*(useEnvVars = false): string = if fileExists(p): yield p elif dirExists(p): + # check if it's a dir where each cert is one file + # named by it's hasg + for fn in joinPath(p, "*.0").walkFiles: + yield p.normalizePathEnd(true) + break for fn in joinPath(p, "*").walkFiles(): + yield fn else: var paths: ptr UncheckedArray[cstring] - size: csize + size: csize_t let err = find_paths_etc( nil, B_FIND_PATH_DATA_DIRECTORY, "ssl/CARootCertificates.pem", B_FIND_PATH_EXISTING_ONLY, paths, size diff --git a/lib/pure/stats.nim b/lib/pure/stats.nim index 58014e503ba2..7f797529d52f 100644 --- a/lib/pure/stats.nim +++ b/lib/pure/stats.nim @@ -55,6 +55,9 @@ runnableExamples: from math import FloatClass, sqrt, pow, round +when defined(nimPreviewSlimSystem): + import std/[assertions, formatfloat] + {.push debugger: off.} # the user does not want to trace a part # of the standard library! {.push checks: off, line_dir: off, stack_trace: off.} diff --git a/lib/pure/streams.nim b/lib/pure/streams.nim index f58273ee8884..50b3085d2b41 100644 --- a/lib/pure/streams.nim +++ b/lib/pure/streams.nim @@ -1197,6 +1197,11 @@ else: # after 1.3 or JS not defined proc ssReadDataStr(s: Stream, buffer: var string, slice: Slice[int]): int = var s = StringStream(s) + when nimvm: + discard + else: + when declared(prepareMutation): + prepareMutation(buffer) # buffer might potentially be a CoW literal with ARC result = min(slice.b + 1 - slice.a, s.data.len - s.pos) if result > 0: jsOrVmBlock: diff --git a/lib/pure/strformat.nim b/lib/pure/strformat.nim index ce3439600830..fe3cfdab0cb8 100644 --- a/lib/pure/strformat.nim +++ b/lib/pure/strformat.nim @@ -180,15 +180,15 @@ The square brackets `[]` indicate an optional element. The optional `align` flag can be one of the following: `<` - Forces the field to be left-aligned within the available +: Forces the field to be left-aligned within the available space. (This is the default for strings.) `>` - Forces the field to be right-aligned within the available space. +: Forces the field to be right-aligned within the available space. (This is the default for numbers.) `^` - Forces the field to be centered within the available space. +: Forces the field to be centered within the available space. Note that unless a minimum field width is defined, the field width will always be the same size as the data to fill it, so that the alignment @@ -705,6 +705,6 @@ macro `&`*(pattern: string{lit}): string = runnableExamples: let x = 7 assert &"{x}\n" == "7\n" # regular string literal - assert &"{x}\n" == "7\n".fmt # `fmt` can be used instead - assert &"{x}\n" != fmt"7\n" # see `fmt` docs, this would use a raw string literal + assert &"{x}\n" == "{x}\n".fmt # `fmt` can be used instead + assert &"{x}\n" != fmt"{x}\n" # see `fmt` docs, this would use a raw string literal strformatImpl(pattern.strVal, '{', '}') diff --git a/lib/pure/strscans.nim b/lib/pure/strscans.nim index 99442075aaf0..8a1ea125fa7b 100644 --- a/lib/pure/strscans.nim +++ b/lib/pure/strscans.nim @@ -286,6 +286,10 @@ efficiency and perform different checks. import macros, parseutils import std/private/since +when defined(nimPreviewSlimSystem): + import std/assertions + + proc conditionsToIfChain(n, idx, res: NimNode; start: int): NimNode = assert n.kind == nnkStmtList if start >= n.len: return newAssignment(res, newLit true) @@ -508,7 +512,7 @@ macro scanTuple*(input: untyped; pattern: static[string]; matcherTypes: varargs[ inc userMatches else: discard inc p - result.add newPar(newCall(ident("scanf"), input, newStrLitNode(pattern))) + result.add nnkTupleConstr.newTree(newCall(ident("scanf"), input, newStrLitNode(pattern))) for arg in arguments: result[^1][0].add arg result[^1].add arg @@ -584,7 +588,7 @@ macro scanp*(input, idx: typed; pattern: varargs[untyped]): bool = action # (x) a # bind action a to (x) - if it[0].kind == nnkPar and it.len == 2: + if it[0].kind in {nnkPar, nnkTupleConstr} and it.len == 2: result = atm(it[0], input, idx, placeholder(it[1], input, idx)) elif it.kind == nnkInfix and it[0].eqIdent"->": # bind matching to some action: diff --git a/lib/pure/strutils.nim b/lib/pure/strutils.nim index bf7bd6aa84c1..b6c0d69173b4 100644 --- a/lib/pure/strutils.nim +++ b/lib/pure/strutils.nim @@ -80,7 +80,8 @@ export toLower, toUpper include "system/inclrtl" import std/private/since -from std/private/strimpl import cmpIgnoreStyleImpl, cmpIgnoreCaseImpl, startsWithImpl, endsWithImpl +from std/private/strimpl import cmpIgnoreStyleImpl, cmpIgnoreCaseImpl, + startsWithImpl, endsWithImpl when defined(nimPreviewSlimSystem): import std/assertions @@ -94,6 +95,15 @@ const Letters* = {'A'..'Z', 'a'..'z'} ## The set of letters. + UppercaseLetters* = {'A'..'Z'} + ## The set of uppercase ASCII letters. + + LowercaseLetters* = {'a'..'z'} + ## The set of lowercase ASCII letters. + + PunctuationChars* = {'!'..'/', ':'..'@', '['..'`', '{'..'~'} + ## The set of all ASCII punctuation characters. + Digits* = {'0'..'9'} ## The set of digits. @@ -110,6 +120,9 @@ const ## The set of characters a newline terminator can start with (carriage ## return, line feed). + PrintableChars* = Letters + Digits + PunctuationChars + Whitespace + ## The set of all printable ASCII characters (letters, digits, whitespace, and punctuation characters). + AllChars* = {'\x00'..'\xFF'} ## A set with all the possible characters. ## @@ -172,7 +185,7 @@ func isLowerAscii*(c: char): bool {.rtl, extern: "nsuIsLowerAsciiChar".} = doAssert isLowerAscii('e') == true doAssert isLowerAscii('E') == false doAssert isLowerAscii('7') == false - return c in {'a'..'z'} + return c in LowercaseLetters func isUpperAscii*(c: char): bool {.rtl, extern: "nsuIsUpperAsciiChar".} = ## Checks whether or not `c` is an upper case character. @@ -186,8 +199,7 @@ func isUpperAscii*(c: char): bool {.rtl, extern: "nsuIsUpperAsciiChar".} = doAssert isUpperAscii('e') == false doAssert isUpperAscii('E') == true doAssert isUpperAscii('7') == false - return c in {'A'..'Z'} - + return c in UppercaseLetters func toLowerAscii*(c: char): char {.rtl, extern: "nsuToLowerAsciiChar".} = ## Returns the lower case version of character `c`. @@ -202,7 +214,7 @@ func toLowerAscii*(c: char): char {.rtl, extern: "nsuToLowerAsciiChar".} = runnableExamples: doAssert toLowerAscii('A') == 'a' doAssert toLowerAscii('e') == 'e' - if c in {'A'..'Z'}: + if c in UppercaseLetters: result = char(uint8(c) xor 0b0010_0000'u8) else: result = c @@ -239,7 +251,7 @@ func toUpperAscii*(c: char): char {.rtl, extern: "nsuToUpperAsciiChar".} = runnableExamples: doAssert toUpperAscii('a') == 'A' doAssert toUpperAscii('E') == 'E' - if c in {'a'..'z'}: + if c in LowercaseLetters: result = char(uint8(c) xor 0b0010_0000'u8) else: result = c @@ -278,7 +290,7 @@ func nimIdentNormalize*(s: string): string = ## except first one. ## ## .. Warning:: Backticks (`) are not handled: they remain *as is* and - ## spaces are preserved. See `nimIdentBackticksNormalize + ## spaces are preserved. See `nimIdentBackticksNormalize ## `_ for ## an alternative approach. runnableExamples: @@ -289,7 +301,7 @@ func nimIdentNormalize*(s: string): string = result[0] = s[0] var j = 1 for i in 1..len(s) - 1: - if s[i] in {'A'..'Z'}: + if s[i] in UppercaseLetters: result[j] = chr(ord(s[i]) + (ord('a') - ord('A'))) inc j elif s[i] != '_': @@ -311,7 +323,7 @@ func normalize*(s: string): string {.rtl, extern: "nsuNormalize".} = result = newString(s.len) var j = 0 for i in 0..len(s) - 1: - if s[i] in {'A'..'Z'}: + if s[i] in UppercaseLetters: result[j] = chr(ord(s[i]) + (ord('a') - ord('A'))) inc j elif s[i] != '_': @@ -1515,7 +1527,8 @@ func delete*(s: var string, slice: Slice[int]) = inc(j) setLen(s, newLen) -func delete*(s: var string, first, last: int) {.rtl, extern: "nsuDelete", deprecated: "use `delete(s, first..last)`".} = +func delete*(s: var string, first, last: int) {.rtl, extern: "nsuDelete", + deprecated: "use `delete(s, first..last)`".} = ## Deletes in `s` the characters at positions `first .. last` (both ends included). runnableExamples("--warning:deprecated:off"): var a = "abracadabra" @@ -1808,26 +1821,44 @@ func join*[T: not string](a: openArray[T], sep: string = ""): string = add(result, $x) type - SkipTable* = array[char, int] + SkipTable* = array[char, int] ## Character table for efficient substring search. func initSkipTable*(a: var SkipTable, sub: string) {.rtl, extern: "nsuInitSkipTable".} = - ## Preprocess table `a` for `sub`. + ## Initializes table `a` for efficient search of substring `sub`. + ## + ## See also: + ## * `initSkipTable func<#initSkipTable,string>`_ + ## * `find func<#find,SkipTable,string,string,Natural,int>`_ + # TODO: this should be the `default()` initializer for the type. let m = len(sub) fill(a, m) for i in 0 ..< m - 1: a[sub[i]] = m - 1 - i -func find*(a: SkipTable, s, sub: string, start: Natural = 0, last = 0): int {. +func initSkipTable*(sub: string): SkipTable {.noinit, rtl, + extern: "nsuInitNewSkipTable".} = + ## Returns a new table initialized for `sub`. + ## + ## See also: + ## * `initSkipTable func<#initSkipTable,SkipTable,string>`_ + ## * `find func<#find,SkipTable,string,string,Natural,int>`_ + initSkipTable(result, sub) + +func find*(a: SkipTable, s, sub: string, start: Natural = 0, last = -1): int {. rtl, extern: "nsuFindStrA".} = ## Searches for `sub` in `s` inside range `start..last` using preprocessed ## table `a`. If `last` is unspecified, it defaults to `s.high` (the last ## element). ## ## Searching is case-sensitive. If `sub` is not in `s`, -1 is returned. + ## + ## See also: + ## * `initSkipTable func<#initSkipTable,string>`_ + ## * `initSkipTable func<#initSkipTable,SkipTable,string>`_ let - last = if last == 0: s.high else: last + last = if last < 0: s.high else: last subLast = sub.len - 1 if subLast == -1: @@ -1837,6 +1868,7 @@ func find*(a: SkipTable, s, sub: string, start: Natural = 0, last = 0): int {. # This is an implementation of the Boyer-Moore Horspool algorithms # https://en.wikipedia.org/wiki/Boyer%E2%80%93Moore%E2%80%93Horspool_algorithm + result = -1 var skip = start while last - skip >= subLast: @@ -1846,71 +1878,72 @@ func find*(a: SkipTable, s, sub: string, start: Natural = 0, last = 0): int {. return skip dec i inc skip, a[s[skip + subLast]] - return -1 when not (defined(js) or defined(nimdoc) or defined(nimscript)): func c_memchr(cstr: pointer, c: char, n: csize_t): pointer {. importc: "memchr", header: "".} - func c_strstr(haystack, needle: cstring): cstring {. - importc: "strstr", header: "".} - const hasCStringBuiltin = true else: const hasCStringBuiltin = false -func find*(s: string, sub: char, start: Natural = 0, last = 0): int {.rtl, +func find*(s: string, sub: char, start: Natural = 0, last = -1): int {.rtl, extern: "nsuFindChar".} = ## Searches for `sub` in `s` inside range `start..last` (both ends included). - ## If `last` is unspecified, it defaults to `s.high` (the last element). + ## If `last` is unspecified or negative, it defaults to `s.high` (the last element). ## ## Searching is case-sensitive. If `sub` is not in `s`, -1 is returned. ## Otherwise the index returned is relative to `s[0]`, not `start`. - ## Use `s[start..last].rfind` for a `start`-origin index. + ## Subtract `start` from the result for a `start`-origin index. ## ## See also: ## * `rfind func<#rfind,string,char,Natural,int>`_ ## * `replace func<#replace,string,char,char>`_ - let last = if last == 0: s.high else: last - when nimvm: + result = -1 + let last = if last < 0: s.high else: last + + template findImpl = for i in int(start)..last: - if sub == s[i]: return i + if s[i] == sub: + return i + + when nimvm: + findImpl() else: when hasCStringBuiltin: - let L = last-start+1 - if L > 0: - let found = c_memchr(s[start].unsafeAddr, sub, cast[csize_t](L)) + let length = last-start+1 + if length > 0: + let found = c_memchr(s[start].unsafeAddr, sub, cast[csize_t](length)) if not found.isNil: return cast[ByteAddress](found) -% cast[ByteAddress](s.cstring) else: - for i in int(start)..last: - if sub == s[i]: return i - return -1 + findImpl() -func find*(s: string, chars: set[char], start: Natural = 0, last = 0): int {. +func find*(s: string, chars: set[char], start: Natural = 0, last = -1): int {. rtl, extern: "nsuFindCharSet".} = ## Searches for `chars` in `s` inside range `start..last` (both ends included). - ## If `last` is unspecified, it defaults to `s.high` (the last element). + ## If `last` is unspecified or negative, it defaults to `s.high` (the last element). ## ## If `s` contains none of the characters in `chars`, -1 is returned. ## Otherwise the index returned is relative to `s[0]`, not `start`. - ## Use `s[start..last].find` for a `start`-origin index. + ## Subtract `start` from the result for a `start`-origin index. ## ## See also: ## * `rfind func<#rfind,string,set[char],Natural,int>`_ ## * `multiReplace func<#multiReplace,string,varargs[]>`_ - let last = if last == 0: s.high else: last + result = -1 + let last = if last < 0: s.high else: last for i in int(start)..last: - if s[i] in chars: return i - return -1 + if s[i] in chars: + return i -func find*(s, sub: string, start: Natural = 0, last = 0): int {.rtl, +func find*(s, sub: string, start: Natural = 0, last = -1): int {.rtl, extern: "nsuFindStr".} = ## Searches for `sub` in `s` inside range `start..last` (both ends included). - ## If `last` is unspecified, it defaults to `s.high` (the last element). + ## If `last` is unspecified or negative, it defaults to `s.high` (the last element). ## ## Searching is case-sensitive. If `sub` is not in `s`, -1 is returned. ## Otherwise the index returned is relative to `s[0]`, not `start`. - ## Use `s[start..last].find` for a `start`-origin index. + ## Subtract `start` from the result for a `start`-origin index. ## ## See also: ## * `rfind func<#rfind,string,string,Natural,int>`_ @@ -1918,28 +1951,7 @@ func find*(s, sub: string, start: Natural = 0, last = 0): int {.rtl, if sub.len > s.len - start: return -1 if sub.len == 1: return find(s, sub[0], start, last) - template useSkipTable {.dirty.} = - var a {.noinit.}: SkipTable - initSkipTable(a, sub) - result = find(a, s, sub, start, last) - - when not hasCStringBuiltin: - useSkipTable() - else: - when nimvm: - useSkipTable() - else: - when hasCStringBuiltin: - if last == 0 and s.len > start: - let found = c_strstr(s[start].unsafeAddr, sub) - if not found.isNil: - result = cast[ByteAddress](found) -% cast[ByteAddress](s.cstring) - else: - result = -1 - else: - useSkipTable() - else: - useSkipTable() + result = find(initSkipTable(sub), s, sub, start, last) func rfind*(s: string, sub: char, start: Natural = 0, last = -1): int {.rtl, extern: "nsuRFindChar".} = @@ -1950,7 +1962,7 @@ func rfind*(s: string, sub: char, start: Natural = 0, last = -1): int {.rtl, ## ## Searching is case-sensitive. If `sub` is not in `s`, -1 is returned. ## Otherwise the index returned is relative to `s[0]`, not `start`. - ## Use `s[start..last].find` for a `start`-origin index. + ## Subtract `start` from the result for a `start`-origin index. ## ## See also: ## * `find func<#find,string,char,Natural,int>`_ @@ -1968,7 +1980,7 @@ func rfind*(s: string, chars: set[char], start: Natural = 0, last = -1): int {. ## ## If `s` contains none of the characters in `chars`, -1 is returned. ## Otherwise the index returned is relative to `s[0]`, not `start`. - ## Use `s[start..last].rfind` for a `start`-origin index. + ## Subtract `start` from the result for a `start`-origin index. ## ## See also: ## * `find func<#find,string,set[char],Natural,int>`_ @@ -1986,12 +1998,13 @@ func rfind*(s, sub: string, start: Natural = 0, last = -1): int {.rtl, ## ## Searching is case-sensitive. If `sub` is not in `s`, -1 is returned. ## Otherwise the index returned is relative to `s[0]`, not `start`. - ## Use `s[start..last].rfind` for a `start`-origin index. + ## Subtract `start` from the result for a `start`-origin index. ## ## See also: ## * `find func<#find,string,string,Natural,int>`_ if sub.len == 0: - return -1 + let rightIndex: Natural = if last < 0: s.len else: last + return max(start, rightIndex) if sub.len > s.len - start: return -1 let last = if last == -1: s.high else: last @@ -2114,8 +2127,7 @@ func replace*(s, sub: string, by = ""): string {.rtl, # copy the rest: add result, substr(s, i) else: - var a {.noinit.}: SkipTable - initSkipTable(a, sub) + var a = initSkipTable(sub) let last = s.high var i = 0 while true: @@ -2155,9 +2167,8 @@ func replaceWord*(s, sub: string, by = ""): string {.rtl, ## replaced. if sub.len == 0: return s const wordChars = {'a'..'z', 'A'..'Z', '0'..'9', '_', '\128'..'\255'} - var a {.noinit.}: SkipTable result = "" - initSkipTable(a, sub) + var a = initSkipTable(sub) var i = 0 let last = s.high let sublen = sub.len @@ -2223,7 +2234,7 @@ func insertSep*(s: string, sep = '_', digits = 3): string {.rtl, doAssert insertSep("1000000") == "1_000_000" result = newStringOfCap(s.len) let hasPrefix = isDigit(s[s.low]) == false - var idx:int + var idx: int if hasPrefix: result.add s[s.low] for i in (s.low + 1)..s.high: @@ -2237,7 +2248,7 @@ func insertSep*(s: string, sep = '_', digits = 3): string {.rtl, result.setLen(L + idx) var j = 0 dec(L) - for i in countdown(partsLen-1,0): + for i in countdown(partsLen-1, 0): if j == digits: result[L + idx] = sep dec(L) @@ -2338,7 +2349,7 @@ func validIdentifier*(s: string): bool {.rtl, extern: "nsuValidIdentifier".} = # floating point formatting: when not defined(js): func c_sprintf(buf, frmt: cstring): cint {.header: "", - importc: "sprintf", varargs} + importc: "sprintf", varargs.} type FloatFormatMode* = enum @@ -2669,8 +2680,8 @@ func findNormalized(x: string, inArray: openArray[string]): int = # security hole... return -1 -func invalidFormatString() {.noinline.} = - raise newException(ValueError, "invalid format string") +func invalidFormatString(formatstr: string) {.noinline.} = + raise newException(ValueError, "invalid format string: " & formatstr) func addf*(s: var string, formatstr: string, a: varargs[string, `$`]) {.rtl, extern: "nsuAddf".} = @@ -2682,7 +2693,7 @@ func addf*(s: var string, formatstr: string, a: varargs[string, `$`]) {.rtl, if formatstr[i] == '$' and i+1 < len(formatstr): case formatstr[i+1] of '#': - if num > a.high: invalidFormatString() + if num > a.high: invalidFormatString(formatstr) add s, a[num] inc i, 2 inc num @@ -2698,7 +2709,7 @@ func addf*(s: var string, formatstr: string, a: varargs[string, `$`]) {.rtl, j = j * 10 + ord(formatstr[i]) - ord('0') inc(i) let idx = if not negative: j-1 else: a.len-j - if idx < 0 or idx > a.high: invalidFormatString() + if idx < 0 or idx > a.high: invalidFormatString(formatstr) add s, a[idx] of '{': var j = i+2 @@ -2715,22 +2726,22 @@ func addf*(s: var string, formatstr: string, a: varargs[string, `$`]) {.rtl, inc(j) if isNumber == 1: let idx = if not negative: k-1 else: a.len-k - if idx < 0 or idx > a.high: invalidFormatString() + if idx < 0 or idx > a.high: invalidFormatString(formatstr) add s, a[idx] else: var x = findNormalized(substr(formatstr, i+2, j-1), a) if x >= 0 and x < high(a): add s, a[x+1] - else: invalidFormatString() + else: invalidFormatString(formatstr) i = j+1 of 'a'..'z', 'A'..'Z', '\128'..'\255', '_': var j = i+1 while j < formatstr.len and formatstr[j] in PatternChars: inc(j) var x = findNormalized(substr(formatstr, i+1, j-1), a) if x >= 0 and x < high(a): add s, a[x+1] - else: invalidFormatString() + else: invalidFormatString(formatstr) i = j else: - invalidFormatString() + invalidFormatString(formatstr) else: add s, formatstr[i] inc(i) diff --git a/lib/pure/sugar.nim b/lib/pure/sugar.nim index d7beef8d9a90..ff2a3bb585c9 100644 --- a/lib/pure/sugar.nim +++ b/lib/pure/sugar.nim @@ -391,6 +391,10 @@ macro collect*(init, body: untyped): untyped {.since: (1, 1).} = macro collect*(body: untyped): untyped {.since: (1, 5).} = ## Same as `collect` but without an `init` parameter. + ## + ## **See also:** + ## * `sequtils.toSeq proc`_ + ## * `sequtils.mapIt template`_ runnableExamples: import std/[sets, tables] let data = @["bird", "word"] @@ -411,10 +415,4 @@ macro collect*(body: untyped): untyped {.since: (1, 5).} = for i, d in data.pairs: {i: d} assert m == {0: "bird", 1: "word"}.toTable - # avoid `collect` when `sequtils.toSeq` suffices: - assert collect(for i in 1..3: i*i) == @[1, 4, 9] # ok in this case - assert collect(for i in 1..3: i) == @[1, 2, 3] # overkill in this case - from std/sequtils import toSeq - assert toSeq(1..3) == @[1, 2, 3] # simpler - result = collectImpl(nil, body) diff --git a/lib/pure/terminal.nim b/lib/pure/terminal.nim index 571c9b13c561..daf470b0903c 100644 --- a/lib/pure/terminal.nim +++ b/lib/pure/terminal.nim @@ -644,9 +644,9 @@ proc setForegroundColor*(f: File, fg: ForegroundColor, bright = false) = 0, # fg8Bit not supported, see `enableTrueColors` instead. 0] # unused if fg == fgDefault: - discard setConsoleTextAttribute(h, toU16(old or defaultForegroundColor)) + discard setConsoleTextAttribute(h, cast[int16](cast[uint16](old) or cast[uint16](defaultForegroundColor))) else: - discard setConsoleTextAttribute(h, toU16(old or lookup[fg])) + discard setConsoleTextAttribute(h, cast[int16](cast[uint16](old) or cast[uint16](lookup[fg]))) else: gFG = ord(fg) if bright: inc(gFG, 60) @@ -673,9 +673,9 @@ proc setBackgroundColor*(f: File, bg: BackgroundColor, bright = false) = 0, # bg8Bit not supported, see `enableTrueColors` instead. 0] # unused if bg == bgDefault: - discard setConsoleTextAttribute(h, toU16(old or defaultBackgroundColor)) + discard setConsoleTextAttribute(h, cast[int16](cast[uint16](old) or cast[uint16](defaultBackgroundColor))) else: - discard setConsoleTextAttribute(h, toU16(old or lookup[bg])) + discard setConsoleTextAttribute(h, cast[int16](cast[uint16](old) or cast[uint16](lookup[bg]))) else: gBG = ord(bg) if bright: inc(gBG, 60) diff --git a/lib/pure/times.nim b/lib/pure/times.nim index b70c5cedc5f5..80be55884ef3 100644 --- a/lib/pure/times.nim +++ b/lib/pure/times.nim @@ -547,8 +547,8 @@ proc getWeeksInIsoYear*(y: IsoYear): IsoWeekRange {.since: (1, 5).} = ## Returns the number of weeks in the specified ISO 8601 week-based year, which can be ## either 53 or 52. runnableExamples: - assert getWeeksInIsoYear(IsoYear(2000)) == 52 - assert getWeeksInIsoYear(IsoYear(2001)) == 53 + assert getWeeksInIsoYear(IsoYear(2019)) == 52 + assert getWeeksInIsoYear(IsoYear(2020)) == 53 var y = int(y) diff --git a/lib/pure/unittest.nim b/lib/pure/unittest.nim index b1c01f8db7d7..593bc1ca4c58 100644 --- a/lib/pure/unittest.nim +++ b/lib/pure/unittest.nim @@ -108,6 +108,9 @@ import std/private/since import std/exitprocs +when defined(nimPreviewSlimSystem): + import std/assertions + import macros, strutils, streams, times, sets, sequtils when declared(stdout): @@ -704,7 +707,9 @@ macro check*(conditions: untyped): untyped = result = quote do: block: `assigns` - if not `check`: + if `check`: + discard + else: checkpoint(`lineinfo` & ": Check failed: " & `callLit`) `printOuts` fail() @@ -720,7 +725,9 @@ macro check*(conditions: untyped): untyped = let callLit = checked.toStrLit result = quote do: - if not `checked`: + if `checked`: + discard + else: checkpoint(`lineinfo` & ": Check failed: " & `callLit`) fail() diff --git a/lib/pure/uri.nim b/lib/pure/uri.nim index 7583dbd1ec07..50b1b9445211 100644 --- a/lib/pure/uri.nim +++ b/lib/pure/uri.nim @@ -39,6 +39,9 @@ runnableExamples: import strutils, parseutils, base64 import std/private/[since, decode_helpers] +when defined(nimPreviewSlimSystem): + import std/assertions + type Url* = distinct string @@ -123,13 +126,13 @@ func decodeUrl*(s: string, decodePlus = true): string = setLen(result, j) func encodeQuery*(query: openArray[(string, string)], usePlus = true, - omitEq = true): string = + omitEq = true, sep = '&'): string = ## Encodes a set of (key, value) parameters into a URL query string. ## ## Every (key, value) pair is URL-encoded and written as `key=value`. If the ## value is an empty string then the `=` is omitted, unless `omitEq` is ## false. - ## The pairs are joined together by a `&` character. + ## The pairs are joined together by the `sep` character. ## ## The `usePlus` parameter is passed down to the `encodeUrl` function that ## is used for the URL encoding of the string values. @@ -140,9 +143,10 @@ func encodeQuery*(query: openArray[(string, string)], usePlus = true, assert encodeQuery({: }) == "" assert encodeQuery({"a": "1", "b": "2"}) == "a=1&b=2" assert encodeQuery({"a": "1", "b": ""}) == "a=1&b" + assert encodeQuery({"a": "1", "b": ""}, omitEq = false, sep = ';') == "a=1;b=" for elem in query: - # Encode the `key = value` pairs and separate them with a '&' - if result.len > 0: result.add('&') + # Encode the `key = value` pairs and separate them with 'sep' + if result.len > 0: result.add(sep) let (key, val) = elem result.add(encodeUrl(key, usePlus)) # Omit the '=' if the value string is empty @@ -150,7 +154,7 @@ func encodeQuery*(query: openArray[(string, string)], usePlus = true, result.add('=') result.add(encodeUrl(val, usePlus)) -iterator decodeQuery*(data: string): tuple[key, value: string] = +iterator decodeQuery*(data: string, sep = '&'): tuple[key, value: string] = ## Reads and decodes the query string `data` and yields the `(key, value)` pairs ## the data consists of. If compiled with `-d:nimLegacyParseQueryStrict`, ## a `UriParseError` is raised when there is an unencoded `=` character in a decoded @@ -158,6 +162,7 @@ iterator decodeQuery*(data: string): tuple[key, value: string] = runnableExamples: import std/sequtils assert toSeq(decodeQuery("foo=1&bar=2=3")) == @[("foo", "1"), ("bar", "2=3")] + assert toSeq(decodeQuery("foo=1;bar=2=3", ';')) == @[("foo", "1"), ("bar", "2=3")] assert toSeq(decodeQuery("&a&=b&=&&")) == @[("", ""), ("a", ""), ("", "b"), ("", ""), ("", "")] proc parseData(data: string, i: int, field: var string, sep: char): int = @@ -186,7 +191,7 @@ iterator decodeQuery*(data: string): tuple[key, value: string] = when defined(nimLegacyParseQueryStrict): i = parseData(data, i, value, '=') else: - i = parseData(data, i, value, '&') + i = parseData(data, i, value, sep) yield (name, value) if i < data.len: when defined(nimLegacyParseQueryStrict): diff --git a/lib/pure/xmltree.nim b/lib/pure/xmltree.nim index fdb2c3fc53ec..72645ef96c31 100644 --- a/lib/pure/xmltree.nim +++ b/lib/pure/xmltree.nim @@ -33,6 +33,10 @@ runnableExamples: import std/private/since import macros, strtabs, strutils +when defined(nimPreviewSlimSystem): + import std/assertions + + type XmlNode* = ref XmlNodeObj ## An XML tree consisting of XML nodes. ## @@ -552,15 +556,15 @@ proc escape*(s: string): string = ## ## Escapes these characters: ## - ## ------------ ------------------- + ## ============ =================== ## char is converted to - ## ------------ ------------------- + ## ============ =================== ## ``<`` ``<`` ## ``>`` ``>`` ## ``&`` ``&`` ## ``"`` ``"`` ## ``'`` ``'`` - ## ------------ ------------------- + ## ============ =================== ## ## You can also use `addEscaped proc <#addEscaped,string,string>`_. result = newStringOfCap(s.len) diff --git a/lib/std/assertions.nim b/lib/std/assertions.nim index 3d2112b1a9a0..0bc4653f21d7 100644 --- a/lib/std/assertions.nim +++ b/lib/std/assertions.nim @@ -85,7 +85,9 @@ template onFailedAssert*(msg, code: untyped): untyped {.dirty.} = onFailedAssert(msg): raise (ref MyError)(msg: msg, lineinfo: instantiationInfo(-2)) doAssertRaises(MyError): doAssert false - template failedAssertImpl(msgIMPL: string): untyped {.dirty.} = + when not defined(nimHasTemplateRedefinitionPragma): + {.pragma: redefine.} + template failedAssertImpl(msgIMPL: string): untyped {.dirty, redefine.} = let msg = msgIMPL code @@ -98,7 +100,7 @@ template doAssertRaises*(exception: typedesc, code: untyped) = var wrong = false const begin = "expected raising '" & astToStr(exception) & "', instead" const msgEnd = " by: " & astToStr(code) - template raisedForeign = raiseAssert(begin & " raised foreign exception" & msgEnd) + template raisedForeign {.gensym.} = raiseAssert(begin & " raised foreign exception" & msgEnd) when Exception is exception: try: if true: diff --git a/lib/std/effecttraits.nim b/lib/std/effecttraits.nim index 635ec82d0cf7..fb057a669640 100644 --- a/lib/std/effecttraits.nim +++ b/lib/std/effecttraits.nim @@ -18,6 +18,7 @@ import macros proc getRaisesListImpl(n: NimNode): NimNode = discard "see compiler/vmops.nim" proc getTagsListImpl(n: NimNode): NimNode = discard "see compiler/vmops.nim" +proc getForbidsListImpl(n: NimNode): NimNode = discard "see compiler/vmops.nim" proc isGcSafeImpl(n: NimNode): bool = discard "see compiler/vmops.nim" proc hasNoSideEffectsImpl(n: NimNode): bool = discard "see compiler/vmops.nim" @@ -37,6 +38,14 @@ proc getTagsList*(fn: NimNode): NimNode = expectKind fn, nnkSym result = getTagsListImpl(fn) +proc getForbidsList*(fn: NimNode): NimNode = + ## Extracts the `.forbids` list of the func/proc/etc `fn`. + ## `fn` has to be a resolved symbol of kind `nnkSym`. This + ## implies that the macro that calls this proc should accept `typed` + ## arguments and not `untyped` arguments. + expectKind fn, nnkSym + result = getForbidsListImpl(fn) + proc isGcSafe*(fn: NimNode): bool = ## Return true if the func/proc/etc `fn` is `gcsafe`. ## `fn` has to be a resolved symbol of kind `nnkSym`. This diff --git a/lib/std/envvars.nim b/lib/std/envvars.nim index 5b135cbd3c08..d7706c17d1db 100644 --- a/lib/std/envvars.nim +++ b/lib/std/envvars.nim @@ -57,15 +57,19 @@ when not defined(nimscript): else: - proc c_getenv(env: cstring): cstring {. - importc: "getenv", header: "".} when defined(windows): proc c_putenv(envstring: cstring): cint {.importc: "_putenv", header: "".} from std/private/win_setenv import setEnvImpl import winlean + proc c_wgetenv(varname: WideCString): WideCString {.importc: "_wgetenv", + header: "".} + proc getEnvImpl(env: cstring): WideCString = c_wgetenv(env.newWideCString) else: + proc c_getenv(env: cstring): cstring {. + importc: "getenv", header: "".} proc c_setenv(envname: cstring, envval: cstring, overwrite: cint): cint {.importc: "setenv", header: "".} proc c_unsetenv(env: cstring): cint {.importc: "unsetenv", header: "".} + proc getEnvImpl(env: cstring): cstring = c_getenv(env) proc getEnv*(key: string, default = ""): string {.tags: [ReadEnvEffect].} = ## Returns the value of the `environment variable`:idx: named `key`. @@ -83,7 +87,7 @@ when not defined(nimscript): assert getEnv("unknownEnv") == "" assert getEnv("unknownEnv", "doesn't exist") == "doesn't exist" - let env = c_getenv(key) + let env = getEnvImpl(key) if env == nil: return default result = $env @@ -99,7 +103,7 @@ when not defined(nimscript): runnableExamples: assert not existsEnv("unknownEnv") - return c_getenv(key) != nil + return getEnvImpl(key) != nil proc putEnv*(key, val: string) {.tags: [WriteEnvEffect].} = ## Sets the value of the `environment variable`:idx: named `key` to `val`. diff --git a/lib/std/formatfloat.nim b/lib/std/formatfloat.nim new file mode 100644 index 000000000000..6f2383760597 --- /dev/null +++ b/lib/std/formatfloat.nim @@ -0,0 +1,142 @@ +# +# +# Nim's Runtime Library +# (c) Copyright 2022 Nim contributors +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +when defined(nimPreviewSlimSystem): + import std/assertions +else: + {.deprecated: "formatfloat is about to move out of system; use `-d:nimPreviewSlimSystem` and import `std/formatfloat`".} + +proc c_memcpy(a, b: pointer, size: csize_t): pointer {.importc: "memcpy", header: "", discardable.} + +proc addCstringN(result: var string, buf: cstring; buflen: int) = + # no nimvm support needed, so it doesn't need to be fast here either + let oldLen = result.len + let newLen = oldLen + buflen + result.setLen newLen + c_memcpy(result[oldLen].addr, buf, buflen.csize_t) + +import std/private/[dragonbox, schubfach] + +proc writeFloatToBufferRoundtrip*(buf: var array[65, char]; value: BiggestFloat): int = + ## This is the implementation to format floats. + ## + ## returns the amount of bytes written to `buf` not counting the + ## terminating '\0' character. + result = toChars(buf, value, forceTrailingDotZero=true) + buf[result] = '\0' + +proc writeFloatToBufferRoundtrip*(buf: var array[65, char]; value: float32): int = + result = float32ToChars(buf, value, forceTrailingDotZero=true) + buf[result] = '\0' + +proc c_sprintf(buf, frmt: cstring): cint {.header: "", + importc: "sprintf", varargs, noSideEffect.} + +proc writeToBuffer(buf: var array[65, char]; value: cstring) = + var i = 0 + while value[i] != '\0': + buf[i] = value[i] + inc i + +proc writeFloatToBufferSprintf*(buf: var array[65, char]; value: BiggestFloat): int = + ## This is the implementation to format floats. + ## + ## returns the amount of bytes written to `buf` not counting the + ## terminating '\0' character. + var n: int = c_sprintf(addr buf, "%.16g", value) + var hasDot = false + for i in 0..n-1: + if buf[i] == ',': + buf[i] = '.' + hasDot = true + elif buf[i] in {'a'..'z', 'A'..'Z', '.'}: + hasDot = true + if not hasDot: + buf[n] = '.' + buf[n+1] = '0' + buf[n+2] = '\0' + result = n + 2 + else: + result = n + # On Windows nice numbers like '1.#INF', '-1.#INF' or '1.#NAN' or 'nan(ind)' + # of '-1.#IND' are produced. + # We want to get rid of these here: + if buf[n-1] in {'n', 'N', 'D', 'd', ')'}: + writeToBuffer(buf, "nan") + result = 3 + elif buf[n-1] == 'F': + if buf[0] == '-': + writeToBuffer(buf, "-inf") + result = 4 + else: + writeToBuffer(buf, "inf") + result = 3 + +proc writeFloatToBuffer*(buf: var array[65, char]; value: BiggestFloat | float32): int {.inline.} = + when defined(nimPreviewFloatRoundtrip) or defined(nimPreviewSlimSystem): + writeFloatToBufferRoundtrip(buf, value) + else: + writeFloatToBufferSprintf(buf, value) + +proc addFloatRoundtrip*(result: var string; x: float | float32) = + when nimvm: + doAssert false + else: + var buffer {.noinit.}: array[65, char] + let n = writeFloatToBufferRoundtrip(buffer, x) + result.addCstringN(cstring(buffer[0].addr), n) + +proc addFloatSprintf*(result: var string; x: float) = + when nimvm: + doAssert false + else: + var buffer {.noinit.}: array[65, char] + let n = writeFloatToBufferSprintf(buffer, x) + result.addCstringN(cstring(buffer[0].addr), n) + +proc nimFloatToString(a: float): cstring = + ## ensures the result doesn't print like an integer, i.e. return 2.0, not 2 + # print `-0.0` properly + asm """ + function nimOnlyDigitsOrMinus(n) { + return n.toString().match(/^-?\d+$/); + } + if (Number.isSafeInteger(`a`)) + `result` = `a` === 0 && 1 / `a` < 0 ? "-0.0" : `a`+".0" + else { + `result` = `a`+"" + if(nimOnlyDigitsOrMinus(`result`)){ + `result` = `a`+".0" + } + } + """ + +proc addFloat*(result: var string; x: float | float32) {.inline.} = + ## Converts float to its string representation and appends it to `result`. + runnableExamples: + var + s = "foo:" + b = 45.67 + s.addFloat(45.67) + assert s == "foo:45.67" + template impl = + when defined(nimPreviewFloatRoundtrip) or defined(nimPreviewSlimSystem): + addFloatRoundtrip(result, x) + else: + addFloatSprintf(result, x) + when defined(js): + when nimvm: impl() + else: + result.add nimFloatToString(x) + else: impl() + +when defined(nimPreviewSlimSystem): + func `$`*(x: float | float32): string = + ## Outplace version of `addFloat`. + result.addFloat(x) diff --git a/lib/std/jsbigints.nim b/lib/std/jsbigints.nim index 27df8fdad145..04578fc87ad9 100644 --- a/lib/std/jsbigints.nim +++ b/lib/std/jsbigints.nim @@ -3,6 +3,9 @@ when not defined(js): {.fatal: "Module jsbigints is designed to be used with the JavaScript backend.".} +when defined(nimPreviewSlimSystem): + import std/assertions + type JsBigIntImpl {.importjs: "bigint".} = int # https://github.com/nim-lang/Nim/pull/16606 type JsBigInt* = distinct JsBigIntImpl ## Arbitrary precision integer for JavaScript target. diff --git a/lib/std/jsfetch.nim b/lib/std/jsfetch.nim index 034bb63328f7..7fe154325373 100644 --- a/lib/std/jsfetch.nim +++ b/lib/std/jsfetch.nim @@ -90,8 +90,9 @@ func newfetchOptions*(metod: HttpMethod; body: cstring; headers: Headers = newHeaders()): FetchOptions = ## Constructor for `FetchOptions`. result = FetchOptions( - body: body, mode: cstring($mode), credentials: cstring($credentials), cache: cstring($cache), referrerPolicy: cstring($referrerPolicy), - keepalive: keepalive, redirect: cstring($redirect), referrer: referrer, integrity: integrity, + body: if metod notin {HttpHead, HttpGet}: body else: nil, + mode: cstring($mode), credentials: cstring($credentials), cache: cstring($cache), referrerPolicy: cstring($referrerPolicy), + keepalive: keepalive, redirect: cstring($redirect), referrer: referrer, integrity: integrity, headers: headers, metod: (case metod of HttpHead: "HEAD".cstring of HttpGet: "GET".cstring diff --git a/lib/std/objectdollar.nim b/lib/std/objectdollar.nim new file mode 100644 index 000000000000..f413bbc46637 --- /dev/null +++ b/lib/std/objectdollar.nim @@ -0,0 +1,11 @@ +import std/private/miscdollars + +proc `$`*[T: object](x: T): string = + ## Generic `$` operator for objects with similar output to + ## `$` for named tuples. + runnableExamples: + type Foo = object + a, b: int + let x = Foo(a: 23, b: 45) + assert $x == "(a: 23, b: 45)" + tupleObjectDollar(result, x) diff --git a/lib/system/dragonbox.nim b/lib/std/private/dragonbox.nim similarity index 98% rename from lib/system/dragonbox.nim rename to lib/std/private/dragonbox.nim index 34ae9e21004a..23adff3857c6 100644 --- a/lib/system/dragonbox.nim +++ b/lib/std/private/dragonbox.nim @@ -30,7 +30,6 @@ when defined(nimPreviewSlimSystem): const dtoaMinBufferLength*: cint = 64 -## -------------------------------------------------------------------------------------------------- ## This file contains an implementation of Junekey Jeon's Dragonbox algorithm. ## ## It is a simplified version of the reference implementation found here: @@ -38,14 +37,13 @@ const ## ## The reference implementation also works with single-precision floating-point numbers and ## has options to configure the rounding mode. -## -------------------------------------------------------------------------------------------------- template dragonbox_Assert*(x: untyped): untyped = assert(x) -## ================================================================================================== -## -## ================================================================================================== +# ================================================================================================== +# +# ================================================================================================== type ValueType* = float @@ -106,10 +104,11 @@ proc isZero*(this: Double): bool {.noSideEffect.} = proc signBit*(this: Double): int {.noSideEffect.} = return ord((this.bits and signMask) != 0) + +# ================================================================================================== +# +# ================================================================================================== ## namespace -## ================================================================================================== -## -## ================================================================================================== ## Returns floor(x / 2^n). ## ## Technically, right-shift of negative integers is implementation defined... @@ -133,9 +132,9 @@ proc floorLog10ThreeQuartersPow2*(e: int32): int32 {.inline.} = dragonbox_Assert(e <= 1500) return floorDivPow2(e * 1262611 - 524031, 22) -## ================================================================================================== -## -## ================================================================================================== +# ================================================================================================== +# +# ================================================================================================== type uint64x2* {.bycopy.} = object @@ -1040,9 +1039,9 @@ proc toDecimal64*(ieeeSignificand: uint64; ieeeExponent: uint64): FloatingDecima dec(q) return FloatingDecimal64(significand: q, exponent: minusK + kappa) -## ================================================================================================== -## ToChars -## ================================================================================================== +# ================================================================================================== +# ToChars +# ================================================================================================== when false: template `+!`(x: cstring; offset: int): cstring = cast[cstring](cast[uint](x) + uint(offset)) diff --git a/lib/std/private/miscdollars.nim b/lib/std/private/miscdollars.nim index 840fedf54e62..47b788ee946f 100644 --- a/lib/std/private/miscdollars.nim +++ b/lib/std/private/miscdollars.nim @@ -12,3 +12,42 @@ template toLocation*(result: var string, file: string | cstring, line: int, col: result.add ", " addInt(result, col) result.add ")" + +when defined(nimHasIsNamedTuple): + proc isNamedTuple(T: typedesc): bool {.magic: "TypeTrait".} +else: + # for bootstrap; remove after release 1.2 + proc isNamedTuple(T: typedesc): bool = + # Taken from typetraits. + when T isnot tuple: result = false + else: + var t: T + for name, _ in t.fieldPairs: + when name == "Field0": + return compiles(t.Field0) + else: + return true + return false + +template tupleObjectDollar*[T: tuple | object](result: var string, x: T) = + result = "(" + const isNamed = T is object or isNamedTuple(typeof(T)) + var count {.used.} = 0 + for name, value in fieldPairs(x): + if count > 0: result.add(", ") + when isNamed: + result.add(name) + result.add(": ") + count.inc + when compiles($value): + when value isnot string and value isnot seq and compiles(value.isNil): + if value.isNil: result.add "nil" + else: result.addQuoted(value) + else: + result.addQuoted(value) + else: + result.add("...") + when not isNamed: + if count == 1: + result.add(",") # $(1,) should print as the semantically legal (1,) + result.add(")") diff --git a/lib/std/private/ntpath.nim b/lib/std/private/ntpath.nim new file mode 100644 index 000000000000..7c8661bb7fb6 --- /dev/null +++ b/lib/std/private/ntpath.nim @@ -0,0 +1,61 @@ +# This module is inspired by Python's `ntpath.py` module. + +import std/[ + strutils, +] + +# Adapted `splitdrive` function from the following commits in Python source +# code: +# 5a607a3ee5e81bdcef3f886f9d20c1376a533df4 (2009): Initial UNC handling (by Mark Hammond) +# 2ba0fd5767577954f331ecbd53596cd8035d7186 (2022): Support for "UNC"-device paths (by Barney Gale) +# +# FAQ: Why use `strip` below? `\\?\UNC` is the start of a "UNC symbolic link", +# which is a special UNC form. Running `strip` differentiates `\\?\UNC\` (a UNC +# symbolic link) from e.g. `\\?\UNCD` (UNCD is the server in the UNC path). +func splitDrive*(p: string): tuple[drive, path: string] = + ## Splits a Windows path into a drive and path part. The drive can be e.g. + ## `C:`. It can also be a UNC path (`\\server\drive\path`). + ## + ## The equivalent `splitDrive` for POSIX systems always returns empty drive. + ## Therefore this proc is only necessary on DOS-like file systems (together + ## with Nim's `doslikeFileSystem` conditional variable). + ## + ## This proc's use case is to extract `path` such that it can be manipulated + ## like a POSIX path. + runnableExamples: + doAssert splitDrive("C:") == ("C:", "") + doAssert splitDrive(r"C:\") == (r"C:", r"\") + doAssert splitDrive(r"\\server\drive\foo\bar") == (r"\\server\drive", r"\foo\bar") + doAssert splitDrive(r"\\?\UNC\server\share\dir") == (r"\\?\UNC\server\share", r"\dir") + + result = ("", p) + if p.len < 2: + return + const sep = '\\' + let normp = p.replace('/', sep) + if p.len > 2 and normp[0] == sep and normp[1] == sep and normp[2] != sep: + + # is a UNC path: + # vvvvvvvvvvvvvvvvvvvv drive letter or UNC path + # \\machine\mountpoint\directory\etc\... + # directory ^^^^^^^^^^^^^^^ + let start = block: + const unc = "\\\\?\\UNC" # Length is 7 + let idx = min(8, normp.len) + if unc == normp[0..= -1500); ## SF_ASSERT(e <= 1500); ## return FloorDivPow2(e * 1262611, 22); ## } +## ``` ## Returns floor(log_10(3/4 2^e)) +## ```c ## static inline int32_t FloorLog10ThreeQuartersPow2(int32_t e) ## { ## SF_ASSERT(e >= -1500); ## SF_ASSERT(e <= 1500); ## return FloorDivPow2(e * 1262611 - 524031, 22); ## } +## ``` ## Returns floor(log_2(10^e)) proc floorLog2Pow10(e: int32): int32 {.inline.} = diff --git a/lib/std/private/strimpl.nim b/lib/std/private/strimpl.nim index 7d42a7cf83b9..7d19825f4c59 100644 --- a/lib/std/private/strimpl.nim +++ b/lib/std/private/strimpl.nim @@ -74,3 +74,40 @@ template endsWithImpl*[T: string | cstring](s, suffix: T) = func cmpNimIdentifier*[T: string | cstring](a, b: T): int = cmpIgnoreStyleImpl(a, b, true) + +func c_memchr(cstr: pointer, c: char, n: csize_t): pointer {. + importc: "memchr", header: "".} +func c_strstr(haystack, needle: cstring): cstring {. + importc: "strstr", header: "".} + + +func find*(s: cstring, sub: char, start: Natural = 0, last = 0): int = + ## Searches for `sub` in `s` inside the range `start..last` (both ends included). + ## If `last` is unspecified, it defaults to `s.high` (the last element). + ## + ## Searching is case-sensitive. If `sub` is not in `s`, -1 is returned. + ## Otherwise the index returned is relative to `s[0]`, not `start`. + ## Use `s[start..last].rfind` for a `start`-origin index. + let last = if last == 0: s.high else: last + let L = last-start+1 + if L > 0: + let found = c_memchr(s[start].unsafeAddr, sub, cast[csize_t](L)) + if not found.isNil: + return cast[ByteAddress](found) -% cast[ByteAddress](s) + return -1 + +func find*(s, sub: cstring, start: Natural = 0, last = 0): int = + ## Searches for `sub` in `s` inside the range `start..last` (both ends included). + ## If `last` is unspecified, it defaults to `s.high` (the last element). + ## + ## Searching is case-sensitive. If `sub` is not in `s`, -1 is returned. + ## Otherwise the index returned is relative to `s[0]`, not `start`. + ## Use `s[start..last].find` for a `start`-origin index. + if sub.len > s.len - start: return -1 + if sub.len == 1: return find(s, sub[0], start, last) + if last == 0 and s.len > start: + let found = c_strstr(s[start].unsafeAddr, sub) + if not found.isNil: + result = cast[ByteAddress](found) -% cast[ByteAddress](s) + else: + result = -1 diff --git a/lib/std/private/win_setenv.nim b/lib/std/private/win_setenv.nim index 0dfe0ed46dc0..89bb0421fd9e 100644 --- a/lib/std/private/win_setenv.nim +++ b/lib/std/private/win_setenv.nim @@ -25,27 +25,30 @@ when not defined(windows): discard else: type wchar_t {.importc: "wchar_t".} = int16 - proc setEnvironmentVariableA*(lpName, lpValue: cstring): int32 {. - stdcall, dynlib: "kernel32", importc: "SetEnvironmentVariableA", sideEffect.} + proc setEnvironmentVariableW*(lpName, lpValue: WideCString): int32 {. + stdcall, dynlib: "kernel32", importc: "SetEnvironmentVariableW", sideEffect.} # same as winlean.setEnvironmentVariableA - proc c_getenv(env: cstring): cstring {.importc: "getenv", header: "".} - proc c_putenv(envstring: cstring): cint {.importc: "_putenv", header: "".} - proc c_wgetenv(varname: ptr wchar_t): ptr wchar_t {.importc: "_wgetenv", header: "".} + proc c_getenv(varname: cstring): cstring {.importc: "getenv", header: "".} + proc c_wputenv(envstring: WideCString): cint {.importc: "_wputenv", header: "".} + proc c_wgetenv(varname: WideCString): WideCString {.importc: "_wgetenv", header: "".} var errno {.importc, header: "".}: cint - var gWenviron {.importc: "_wenviron".}: ptr ptr wchar_t + var genviron {.importc: "_environ".}: ptr ptr char # xxx `ptr UncheckedArray[WideCString]` did not work - proc mbstowcs(wcstr: ptr wchar_t, mbstr: cstring, count: csize_t): csize_t {.importc: "mbstowcs", header: "".} + proc wcstombs(wcstr: ptr char, mbstr: WideCString, count: csize_t): csize_t {.importc, header: "".} # xxx cint vs errno_t? proc setEnvImpl*(name: string, value: string, overwrite: cint): cint = const EINVAL = cint(22) - if overwrite == 0 and c_getenv(cstring(name)) != nil: return 0 + let wideName = newWideCString(name) + if overwrite == 0 and c_wgetenv(wideName) != nil: + return 0 + if value != "": let envstring = name & "=" & value - let e = c_putenv(cstring(envstring)) + let e = c_wputenv(newWideCString(envstring)) if e != 0: errno = EINVAL return -1 @@ -57,40 +60,44 @@ else: so we have to do these terrible things. ]# let envstring = name & "= " - if c_putenv(cstring(envstring)) != 0: + if c_wputenv(newWideCString(envstring)) != 0: errno = EINVAL return -1 # Here lies the documentation we blatently ignore to make this work. - var s = c_getenv(cstring(name)) - s[0] = '\0' + var s = c_wgetenv(wideName) + s[0] = Utf16Char('\0') #[ This would result in a double null termination, which normally signifies the end of the environment variable list, so we stick a completely empty environment variable into the list instead. ]# - s = c_getenv(cstring(name)) - s[1] = '=' + s = c_wgetenv(wideName) + s[1] = Utf16Char('=') #[ - If gWenviron is null, the wide environment has not been initialized + If genviron is null, the MBCS environment has not been initialized yet, and we don't need to try to update it. We have to do this otherwise - we'd be forcing the initialization and maintenance of the wide environment + we'd be forcing the initialization and maintenance of the MBCS environment even though it's never actually used in most programs. ]# - if gWenviron != nil: - # var buf: array[MAX_ENV + 1, WideCString] - let requiredSize = mbstowcs(nil, cstring(name), 0).int - var buf = newSeq[Utf16Char](requiredSize + 1) - let buf2 = cast[ptr wchar_t](buf[0].addr) - if mbstowcs(buf2, cstring(name), csize_t(requiredSize + 1)) == csize_t(high(uint)): - errno = EINVAL - return -1 - var ptrToEnv = cast[WideCString](c_wgetenv(buf2)) - ptrToEnv[0] = '\0'.Utf16Char - ptrToEnv = cast[WideCString](c_wgetenv(buf2)) - ptrToEnv[1] = '='.Utf16Char + if genviron != nil: + + # wcstombs returns `high(csize_t)` if any characters cannot be represented + # in the current codepage. Skip updating MBCS environment in this case. + # For some reason, second `wcstombs` can find non-convertible characters + # that the first `wcstombs` cannot. + let requiredSizeS = wcstombs(nil, wideName, 0) + if requiredSizeS != high(csize_t): + let requiredSize = requiredSizeS.int + var buf = newSeq[char](requiredSize + 1) + let buf2 = buf[0].addr + if wcstombs(buf2, wideName, csize_t(requiredSize + 1)) != high(csize_t): + var ptrToEnv = c_getenv(buf2) + ptrToEnv[0] = '\0' + ptrToEnv = c_getenv(buf2) + ptrToEnv[1] = '=' # And now, we have to update the outer environment to have a proper empty value. - if setEnvironmentVariableA(cstring(name), cstring(value)) == 0: + if setEnvironmentVariableW(wideName, value.newWideCString) == 0: errno = EINVAL return -1 return 0 diff --git a/lib/std/strbasics.nim b/lib/std/strbasics.nim index 6f6db5c37070..be1dd7a586a4 100644 --- a/lib/std/strbasics.nim +++ b/lib/std/strbasics.nim @@ -11,6 +11,10 @@ ## ## Experimental API, subject to change. +when defined(nimPreviewSlimSystem): + import std/assertions + + const whitespaces = {' ', '\t', '\v', '\r', '\l', '\f'} proc add*(x: var string, y: openArray[char]) = diff --git a/lib/std/sums.nim b/lib/std/sums.nim index b68858ef7d4e..a6ce1b85d16e 100644 --- a/lib/std/sums.nim +++ b/lib/std/sums.nim @@ -8,6 +8,8 @@ ## Accurate summation functions. +{.deprecated: "use the nimble package `sums` instead.".} + runnableExamples: import std/math diff --git a/lib/std/syncio.nim b/lib/std/syncio.nim index d7019e20b1fc..fc132bba7d28 100644 --- a/lib/std/syncio.nim +++ b/lib/std/syncio.nim @@ -11,7 +11,7 @@ include system/inclrtl import std/private/since -import system/formatfloat +import std/formatfloat # ----------------- IO Part ------------------------------------------------ type @@ -39,6 +39,13 @@ type FileHandle* = cint ## type that represents an OS file handle; this is ## useful for low-level file access + FileSeekPos* = enum ## Position relative to which seek should happen. + # The values are ordered so that they match with stdio + # SEEK_SET, SEEK_CUR and SEEK_END respectively. + fspSet ## Seek to absolute value + fspCur ## Seek relative to current position + fspEnd ## Seek relative to end + # text file handling: when not defined(nimscript) and not defined(js): # duplicated between io and ansi_c @@ -142,13 +149,6 @@ proc c_fprintf(f: File, frmt: cstring): cint {. proc c_fputc(c: char, f: File): cint {. importc: "fputc", header: "".} -# When running nim in android app, stdout goes nowhere, so echo gets ignored -# To redirect echo to the android logcat, use -d:androidNDK -when defined(androidNDK): - const ANDROID_LOG_VERBOSE = 2.cint - proc android_log_print(prio: cint, tag: cstring, fmt: cstring): cint - {.importc: "__android_log_print", header: "", varargs, discardable.} - template sysFatal(exc, msg) = raise newException(exc, msg) @@ -791,53 +791,6 @@ proc setStdIoUnbuffered*() {.tags: [], benign.} = when declared(stdin): discard c_setvbuf(stdin, nil, IONBF, 0) -when declared(stdout): - when defined(windows) and compileOption("threads"): - proc addSysExitProc(quitProc: proc() {.noconv.}) {.importc: "atexit", header: "".} - - const insideRLocksModule = false - include "system/syslocks" - - - var echoLock: SysLock - initSysLock echoLock - addSysExitProc(proc() {.noconv.} = deinitSys(echoLock)) - - const stdOutLock = not defined(windows) and - not defined(dos) and - not defined(android) and - not defined(nintendoswitch) and - not defined(freertos) and - not defined(zephyr) and - hostOS != "any" - - proc echoBinSafe(args: openArray[string]) {.compilerproc.} = - when defined(androidNDK): - var s = "" - for arg in args: - s.add arg - android_log_print(ANDROID_LOG_VERBOSE, "nim", s) - else: - # flockfile deadlocks some versions of Android 5.x.x - when stdOutLock: - proc flockfile(f: File) {.importc, nodecl.} - proc funlockfile(f: File) {.importc, nodecl.} - flockfile(stdout) - when defined(windows) and compileOption("threads"): - acquireSys echoLock - for s in args: - when defined(windows): - writeWindows(stdout, s) - else: - discard c_fwrite(s.cstring, cast[csize_t](s.len), 1, stdout) - const linefeed = "\n" - discard c_fwrite(linefeed.cstring, linefeed.len, 1, stdout) - discard c_fflush(stdout) - when stdOutLock: - funlockfile(stdout) - when defined(windows) and compileOption("threads"): - releaseSys echoLock - when defined(windows) and not defined(nimscript) and not defined(js): # work-around C's sucking abstraction: @@ -961,3 +914,7 @@ iterator lines*(f: File): string {.tags: [ReadIOEffect].} = result.lines += 1 var res = newStringOfCap(80) while f.readLine(res): yield res + +template `&=`*(f: File, x: typed) = + ## An alias for `write`. + write(f, x) diff --git a/lib/std/sysrand.nim b/lib/std/sysrand.nim index 1b7b2c0241ac..ff62c920b8dd 100644 --- a/lib/std/sysrand.nim +++ b/lib/std/sysrand.nim @@ -62,6 +62,9 @@ when not defined(js): when defined(posix): import posix +when defined(nimPreviewSlimSystem): + import std/assertions + const batchImplOS = defined(freebsd) or defined(openbsd) or defined(zephyr) or (defined(macosx) and not defined(ios)) batchSize {.used.} = 256 @@ -170,9 +173,8 @@ elif defined(linux) and not defined(nimNoGetRandom) and not defined(emscripten): const syscallHeader = """#include #include """ - proc syscall( - n: clong, buf: pointer, bufLen: cint, flags: cuint - ): clong {.importc: "syscall", header: syscallHeader.} + proc syscall(n: clong): clong {. + importc: "syscall", varargs, header: syscallHeader.} # When reading from the urandom source (GRND_RANDOM is not set), # getrandom() will block until the entropy pool has been # initialized (unless the GRND_NONBLOCK flag was specified). If a @@ -211,7 +213,7 @@ elif defined(zephyr): proc sys_csrand_get(dst: pointer, length: csize_t): cint {.importc: "sys_csrand_get", header: "".} # Fill the destination buffer with cryptographically secure # random data values - # + # proc getRandomImpl(p: pointer, size: int): int {.inline.} = # 0 if success, -EIO if entropy reseed error diff --git a/lib/std/tasks.nim b/lib/std/tasks.nim index 7b931ab14e72..ac35e26bfa83 100644 --- a/lib/std/tasks.nim +++ b/lib/std/tasks.nim @@ -13,6 +13,9 @@ import std/[macros, isolation, typetraits] import system/ansi_c +when defined(nimPreviewSlimSystem): + import std/assertions + export isolation diff --git a/lib/std/tempfiles.nim b/lib/std/tempfiles.nim index ce84c2a3739f..ee42f8a5c99f 100644 --- a/lib/std/tempfiles.nim +++ b/lib/std/tempfiles.nim @@ -19,6 +19,8 @@ See also: import os, random +when defined(nimPreviewSlimSystem): + import std/syncio const maxRetry = 10000 diff --git a/lib/system.nim b/lib/system.nim index 4080fee06470..2de70ecadf0c 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -22,97 +22,52 @@ ## .. include:: ./system_overview.rst -type - float* {.magic: Float.} ## Default floating point type. - float32* {.magic: Float32.} ## 32 bit floating point type. - float64* {.magic: Float.} ## 64 bit floating point type. - -# 'float64' is now an alias to 'float'; this solves many problems +include "system/basic_types" -type - char* {.magic: Char.} ## Built-in 8 bit character type (unsigned). - string* {.magic: String.} ## Built-in string type. - cstring* {.magic: Cstring.} ## Built-in cstring (*compatible string*) type. - pointer* {.magic: Pointer.} ## Built-in pointer type, use the `addr` - ## operator to get a pointer to a variable. +include "system/compilation" - typedesc* {.magic: TypeDesc.} ## Meta type to denote a type description. +{.push warning[GcMem]: off, warning[Uninit]: off.} +# {.push hints: off.} type - `ptr`*[T] {.magic: Pointer.} ## Built-in generic untraced pointer type. - `ref`*[T] {.magic: Pointer.} ## Built-in generic traced pointer type. - - `nil` {.magic: "Nil".} - - void* {.magic: "VoidType".} ## Meta type to denote the absence of any type. - auto* {.magic: Expr.} ## Meta type for automatic type determination. - any* {.deprecated: "Deprecated since v1.5; Use auto instead.".} = distinct auto ## Deprecated; Use `auto` instead. See https://github.com/nim-lang/RFCs/issues/281 - untyped* {.magic: Expr.} ## Meta type to denote an expression that - ## is not resolved (for templates). - typed* {.magic: Stmt.} ## Meta type to denote an expression that - ## is resolved (for templates). + `static`*[T] {.magic: "Static".} + ## Meta type representing all values that can be evaluated at compile-time. + ## + ## The type coercion `static(x)` can be used to force the compile-time + ## evaluation of the given expression `x`. -include "system/basic_types" + `type`*[T] {.magic: "Type".} + ## Meta type representing the type of all type values. + ## + ## The coercion `type(x)` can be used to obtain the type of the given + ## expression `x`. +type + TypeOfMode* = enum ## Possible modes of `typeof`. + typeOfProc, ## Prefer the interpretation that means `x` is a proc call. + typeOfIter ## Prefer the interpretation that means `x` is an iterator call. -proc runnableExamples*(rdoccmd = "", body: untyped) {.magic: "RunnableExamples".} = - ## A section you should use to mark `runnable example`:idx: code with. - ## - ## - In normal debug and release builds code within - ## a `runnableExamples` section is ignored. - ## - The documentation generator is aware of these examples and considers them - ## part of the `##` doc comment. As the last step of documentation - ## generation each runnableExample is put in its own file `$file_examples$i.nim`, - ## compiled and tested. The collected examples are - ## put into their own module to ensure the examples do not refer to - ## non-exported symbols. - runnableExamples: - proc timesTwo*(x: int): int = - ## This proc doubles a number. - runnableExamples: - # at module scope - const exported* = 123 - assert timesTwo(5) == 10 - block: # at block scope - defer: echo "done" - runnableExamples "-d:foo -b:cpp": - import std/compilesettings - assert querySetting(backend) == "cpp" - assert defined(foo) - runnableExamples "-r:off": ## this one is only compiled - import std/browsers - openDefaultBrowser "https://forum.nim-lang.org/" - 2 * x - -proc compileOption*(option: string): bool {. - magic: "CompileOption", noSideEffect.} = - ## Can be used to determine an `on|off` compile-time option. - ## - ## See also: - ## * `compileOption <#compileOption,string,string>`_ for enum options - ## * `defined <#defined,untyped>`_ - ## * `std/compilesettings module `_ - runnableExamples("--floatChecks:off"): - static: doAssert not compileOption("floatchecks") - {.push floatChecks: on.} - static: doAssert compileOption("floatchecks") - # floating point NaN and Inf checks enabled in this scope - {.pop.} - -proc compileOption*(option, arg: string): bool {. - magic: "CompileOptionArg", noSideEffect.} = - ## Can be used to determine an enum compile-time option. - ## - ## See also: - ## * `compileOption <#compileOption,string>`_ for `on|off` options - ## * `defined <#defined,untyped>`_ - ## * `std/compilesettings module `_ +proc typeof*(x: untyped; mode = typeOfIter): typedesc {. + magic: "TypeOf", noSideEffect, compileTime.} = + ## Builtin `typeof` operation for accessing the type of an expression. + ## Since version 0.20.0. runnableExamples: - when compileOption("opt", "size") and compileOption("gc", "boehm"): - discard "compiled with optimization for size and uses Boehm's GC" + proc myFoo(): float = 0.0 + iterator myFoo(): string = yield "abc" + iterator myFoo2(): string = yield "abc" + iterator myFoo3(): string {.closure.} = yield "abc" + doAssert type(myFoo()) is string + doAssert typeof(myFoo()) is string + doAssert typeof(myFoo(), typeOfIter) is string + doAssert typeof(myFoo3) is "iterator" -{.push warning[GcMem]: off, warning[Uninit]: off.} -# {.push hints: off.} + doAssert typeof(myFoo(), typeOfProc) is float + doAssert typeof(0.0, typeOfProc) is float + doAssert typeof(myFoo3, typeOfProc) is "iterator" + doAssert not compiles(typeof(myFoo2(), typeOfProc)) + # this would give: Error: attempting to call routine: 'myFoo2' + # since `typeOfProc` expects a typed expression and `myFoo2()` can + # only be used in a `for` context. proc `or`*(a, b: typedesc): typedesc {.magic: "TypeTrait", noSideEffect.} ## Constructs an `or` meta class. @@ -123,32 +78,6 @@ proc `and`*(a, b: typedesc): typedesc {.magic: "TypeTrait", noSideEffect.} proc `not`*(a: typedesc): typedesc {.magic: "TypeTrait", noSideEffect.} ## Constructs an `not` meta class. - -type - SomeFloat* = float|float32|float64 - ## Type class matching all floating point number types. - - SomeNumber* = SomeInteger|SomeFloat - ## Type class matching all number types. - -proc defined*(x: untyped): bool {.magic: "Defined", noSideEffect, compileTime.} - ## Special compile-time procedure that checks whether `x` is - ## defined. - ## - ## See also: - ## * `compileOption <#compileOption,string>`_ for `on|off` options - ## * `compileOption <#compileOption,string,string>`_ for enum options - ## * `define pragmas `_ - ## - ## `x` is an external symbol introduced through the compiler's - ## `-d:x switch `_ to enable - ## build time conditionals: - ## - ## .. code-block:: Nim - ## when not defined(release): - ## # Do here programmer friendly expensive sanity checks. - ## # Put here the normal code - when defined(nimHasIterable): type iterable*[T] {.magic: IterableType.} ## Represents an expression that yields `T` @@ -165,31 +94,6 @@ else: OrdinalImpl[T] {.magic: Ordinal.} Ordinal* = OrdinalImpl | uint | uint64 -when defined(nimHasDeclaredMagic): - proc declared*(x: untyped): bool {.magic: "Declared", noSideEffect, compileTime.} - ## Special compile-time procedure that checks whether `x` is - ## declared. `x` has to be an identifier or a qualified identifier. - ## - ## See also: - ## * `declaredInScope <#declaredInScope,untyped>`_ - ## - ## This can be used to check whether a library provides a certain - ## feature or not: - ## - ## .. code-block:: Nim - ## when not declared(strutils.toUpper): - ## # provide our own toUpper proc here, because strutils is - ## # missing it. -else: - proc declared*(x: untyped): bool {.magic: "Defined", noSideEffect, compileTime.} - -when defined(nimHasDeclaredMagic): - proc declaredInScope*(x: untyped): bool {.magic: "DeclaredInScope", noSideEffect, compileTime.} - ## Special compile-time procedure that checks whether `x` is - ## declared in the current scope. `x` has to be an identifier. -else: - proc declaredInScope*(x: untyped): bool {.magic: "DefinedInScope", noSideEffect, compileTime.} - proc `addr`*[T](x: T): ptr T {.magic: "Addr", noSideEffect.} = ## Builtin `addr` operator for taking the address of a memory location. ## @@ -202,15 +106,17 @@ proc `addr`*[T](x: T): ptr T {.magic: "Addr", noSideEffect.} = ## ## Cannot be overloaded. ## - ## .. code-block:: Nim - ## var - ## buf: seq[char] = @['a','b','c'] - ## p = buf[1].addr - ## echo p.repr # ref 0x7faa35c40059 --> 'b' - ## echo p[] # b + ## ``` + ## var + ## buf: seq[char] = @['a','b','c'] + ## p = buf[1].addr + ## echo p.repr # ref 0x7faa35c40059 --> 'b' + ## echo p[] # b + ## ``` discard -proc unsafeAddr*[T](x: T): ptr T {.magic: "Addr", noSideEffect.} = +proc unsafeAddr*[T](x: T): ptr T {.magic: "Addr", noSideEffect, + deprecated: "'unsafeAddr' is a deprecated alias for 'addr'".} = ## Builtin `addr` operator for taking the address of a memory ## location. ## @@ -224,64 +130,24 @@ proc unsafeAddr*[T](x: T): ptr T {.magic: "Addr", noSideEffect.} = ## Cannot be overloaded. discard -type - `static`*[T] {.magic: "Static".} - ## Meta type representing all values that can be evaluated at compile-time. - ## - ## The type coercion `static(x)` can be used to force the compile-time - ## evaluation of the given expression `x`. - - `type`*[T] {.magic: "Type".} - ## Meta type representing the type of all type values. - ## - ## The coercion `type(x)` can be used to obtain the type of the given - ## expression `x`. - -type - TypeOfMode* = enum ## Possible modes of `typeof`. - typeOfProc, ## Prefer the interpretation that means `x` is a proc call. - typeOfIter ## Prefer the interpretation that means `x` is an iterator call. - -proc typeof*(x: untyped; mode = typeOfIter): typedesc {. - magic: "TypeOf", noSideEffect, compileTime.} = - ## Builtin `typeof` operation for accessing the type of an expression. - ## Since version 0.20.0. - runnableExamples: - proc myFoo(): float = 0.0 - iterator myFoo(): string = yield "abc" - iterator myFoo2(): string = yield "abc" - iterator myFoo3(): string {.closure.} = yield "abc" - doAssert type(myFoo()) is string - doAssert typeof(myFoo()) is string - doAssert typeof(myFoo(), typeOfIter) is string - doAssert typeof(myFoo3) is "iterator" - - doAssert typeof(myFoo(), typeOfProc) is float - doAssert typeof(0.0, typeOfProc) is float - doAssert typeof(myFoo3, typeOfProc) is "iterator" - doAssert not compiles(typeof(myFoo2(), typeOfProc)) - # this would give: Error: attempting to call routine: 'myFoo2' - # since `typeOfProc` expects a typed expression and `myFoo2()` can - # only be used in a `for` context. const ThisIsSystem = true proc internalNew*[T](a: var ref T) {.magic: "New", noSideEffect.} ## Leaked implementation detail. Do not use. -when true: - proc new*[T](a: var ref T, finalizer: proc (x: ref T) {.nimcall.}) {. - magic: "NewFinalize", noSideEffect.} - ## Creates a new object of type `T` and returns a safe (traced) - ## reference to it in `a`. - ## - ## When the garbage collector frees the object, `finalizer` is called. - ## The `finalizer` may not keep a reference to the - ## object pointed to by `x`. The `finalizer` cannot prevent the GC from - ## freeing the object. - ## - ## **Note**: The `finalizer` refers to the type `T`, not to the object! - ## This means that for each object of type `T` the finalizer will be called! +proc new*[T](a: var ref T, finalizer: proc (x: ref T) {.nimcall.}) {. + magic: "NewFinalize", noSideEffect.} + ## Creates a new object of type `T` and returns a safe (traced) + ## reference to it in `a`. + ## + ## When the garbage collector frees the object, `finalizer` is called. + ## The `finalizer` may not keep a reference to the + ## object pointed to by `x`. The `finalizer` cannot prevent the GC from + ## freeing the object. + ## + ## **Note**: The `finalizer` refers to the type `T`, not to the object! + ## This means that for each object of type `T` the finalizer will be called! proc wasMoved*[T](obj: var T) {.magic: "WasMoved", noSideEffect.} = ## Resets an object `obj` to its initial (binary zero) value to signify @@ -321,56 +187,57 @@ proc high*[T: Ordinal|enum|range](x: T): T {.magic: "High", noSideEffect, ## **This proc is deprecated**, use this one instead: ## * `high(typedesc) <#high,typedesc[T]>`_ ## - ## .. code-block:: Nim - ## high(2) # => 9223372036854775807 + ## ``` + ## high(2) # => 9223372036854775807 + ## ``` proc high*[T: Ordinal|enum|range](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:. + ## ``` + ## high(int) # => 9223372036854775807 + ## ``` ## ## See also: ## * `low(typedesc) <#low,typedesc[T]>`_ - ## - ## .. 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`. + ## ``` + ## var s = @[1, 2, 3, 4, 5, 6, 7] + ## high(s) # => 6 + ## for i in low(s)..high(s): + ## echo s[i] + ## ``` ## ## See also: ## * `low(openArray) <#low,openArray[T]>`_ - ## - ## .. 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`. ## ## For empty arrays, the return type is `int`. + ## ``` + ## var arr = [1, 2, 3, 4, 5, 6, 7] + ## high(arr) # => 6 + ## for i in low(arr)..high(arr): + ## echo arr[i] + ## ``` ## ## See also: ## * `low(array) <#low,array[I,T]>`_ - ## - ## .. code-block:: Nim - ## var arr = [1, 2, 3, 4, 5, 6, 7] - ## high(arr) # => 6 - ## for i in low(arr)..high(arr): - ## echo arr[i] proc high*[I, T](x: typedesc[array[I, T]]): I {.magic: "High", noSideEffect.} ## Returns the highest possible index of an array type. ## ## For empty arrays, the return type is `int`. + ## ``` + ## high(array[7, int]) # => 6 + ## ``` ## ## See also: ## * `low(typedesc[array]) <#low,typedesc[array[I,T]]>`_ - ## - ## .. 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`. @@ -381,13 +248,13 @@ proc high*(x: cstring): int {.magic: "High", noSideEffect.} proc high*(x: string): int {.magic: "High", noSideEffect.} ## Returns the highest possible index of a string `x`. + ## ``` + ## var str = "Hello world!" + ## high(str) # => 11 + ## ``` ## ## See also: ## * `low(string) <#low,string>`_ - ## - ## .. code-block:: Nim - ## var str = "Hello world!" - ## high(str) # => 11 proc low*[T: Ordinal|enum|range](x: T): T {.magic: "Low", noSideEffect, deprecated: "Deprecated since v1.4; there should not be `low(value)`. Use `low(type)`.".} @@ -397,56 +264,57 @@ proc low*[T: Ordinal|enum|range](x: T): T {.magic: "Low", noSideEffect, ## **This proc is deprecated**, use this one instead: ## * `low(typedesc) <#low,typedesc[T]>`_ ## - ## .. code-block:: Nim - ## low(2) # => -9223372036854775808 + ## ``` + ## low(2) # => -9223372036854775808 + ## ``` proc low*[T: Ordinal|enum|range](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:. + ## ``` + ## low(int) # => -9223372036854775808 + ## ``` ## ## See also: ## * `high(typedesc) <#high,typedesc[T]>`_ - ## - ## .. 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`. + ## ``` + ## var s = @[1, 2, 3, 4, 5, 6, 7] + ## low(s) # => 0 + ## for i in low(s)..high(s): + ## echo s[i] + ## ``` ## ## See also: ## * `high(openArray) <#high,openArray[T]>`_ - ## - ## .. code-block:: Nim - ## var s = @[1, 2, 3, 4, 5, 6, 7] - ## low(s) # => 0 - ## for i in low(s)..high(s): - ## echo s[i] proc low*[I, T](x: array[I, T]): I {.magic: "Low", noSideEffect.} ## Returns the lowest possible index of an array `x`. ## ## For empty arrays, the return type is `int`. + ## ``` + ## var arr = [1, 2, 3, 4, 5, 6, 7] + ## low(arr) # => 0 + ## for i in low(arr)..high(arr): + ## echo arr[i] + ## ``` ## ## See also: ## * `high(array) <#high,array[I,T]>`_ - ## - ## .. code-block:: Nim - ## var arr = [1, 2, 3, 4, 5, 6, 7] - ## low(arr) # => 0 - ## for i in low(arr)..high(arr): - ## echo arr[i] proc low*[I, T](x: typedesc[array[I, T]]): I {.magic: "Low", noSideEffect.} ## Returns the lowest possible index of an array type. ## ## For empty arrays, the return type is `int`. + ## ``` + ## low(array[7, int]) # => 0 + ## ``` ## ## See also: ## * `high(typedesc[array]) <#high,typedesc[array[I,T]]>`_ - ## - ## .. 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`. @@ -456,23 +324,24 @@ proc low*(x: cstring): int {.magic: "Low", noSideEffect.} proc low*(x: string): int {.magic: "Low", noSideEffect.} ## Returns the lowest possible index of a string `x`. + ## ``` + ## var str = "Hello world!" + ## low(str) # => 0 + ## ``` ## ## See also: ## * `high(string) <#high,string>`_ - ## - ## .. 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 only changes the semantics for sequences and strings - ## (and types which contain those). - ## - ## Be careful with the changed semantics though! - ## There is a reason why the default assignment does a deep copy of sequences - ## and strings. +when not defined(gcArc) and not defined(gcOrc): + proc shallowCopy*[T](x: var T, y: T) {.noSideEffect, magic: "ShallowCopy".} + ## Use this instead of `=` for a `shallow copy`:idx:. + ## + ## The shallow copy only changes the semantics for sequences and strings + ## (and types which contain those). + ## + ## Be careful with the changed semantics though! + ## There is a reason why the default assignment does a deep copy of sequences + ## and strings. # :array|openArray|string|seq|cstring|tuple proc `[]`*[I: Ordinal;T](a: T; i: I): T {. @@ -490,9 +359,12 @@ proc arrPut[I: Ordinal;T,S](a: T; i: I; proc `=destroy`*[T](x: var T) {.inline, magic: "Destroy".} = ## Generic `destructor`:idx: implementation that can be overridden. discard -proc `=sink`*[T](x: var T; y: T) {.inline, magic: "Asgn".} = +proc `=sink`*[T](x: var T; y: T) {.inline, nodestroy, magic: "Asgn".} = ## Generic `sink`:idx: implementation that can be overridden. - shallowCopy(x, y) + when defined(gcArc) or defined(gcOrc): + x = y + else: + shallowCopy(x, y) when defined(nimHasTrace): proc `=trace`*[T](x: var T; env: pointer) {.inline, magic: "Trace".} = @@ -511,19 +383,19 @@ proc `..`*[T, U](a: sink T, b: sink U): HSlice[T, U] {.noSideEffect, inline, mag ## ## Slices can also be used in the set constructor and in ordinal case ## statements, but then they are special-cased by the compiler. - ## - ## .. code-block:: Nim + ## ``` ## let a = [10, 20, 30, 40, 50] ## echo a[2 .. 3] # @[30, 40] + ## ``` result = HSlice[T, U](a: a, b: b) proc `..`*[T](b: sink T): HSlice[int, T] {.noSideEffect, inline, magic: "DotDot", deprecated: "replace `..b` with `0..b`".} = ## Unary `slice`:idx: operator that constructs an interval `[default(int), b]`. - ## - ## .. code-block:: Nim + ## ``` ## let a = [10, 20, 30, 40, 50] ## echo a[.. 2] # @[10, 20, 30] + ## ``` result = HSlice[int, T](a: 0, b: b) when defined(hotCodeReloading): @@ -531,12 +403,6 @@ when defined(hotCodeReloading): else: {.pragma: hcrInline.} -{.push profiler: off.} -let nimvm* {.magic: "Nimvm", compileTime.}: bool = false - ## May be used only in `when` expression. - ## It is true in Nim VM context and false otherwise. -{.pop.} - include "system/arithmetics" include "system/comparisons" @@ -584,6 +450,7 @@ type ## is an `int` type ranging from one to the maximum value ## of an `int`. This type is often useful for documentation and debugging. +type RootObj* {.compilerproc, inheritable.} = object ## The root of Nim's object hierarchy. ## @@ -591,8 +458,66 @@ type ## However, objects that have no ancestor are also allowed. RootRef* = ref RootObj ## Reference to `RootObj`. +const NimStackTraceMsgs = + when defined(nimHasStacktraceMsgs): compileOption("stacktraceMsgs") + else: false + +type + RootEffect* {.compilerproc.} = object of RootObj ## \ + ## Base effect class. + ## + ## Each effect should inherit from `RootEffect` unless you know what + ## you're doing. + +type + StackTraceEntry* = object ## In debug mode exceptions store the stack trace that led + ## to them. A `StackTraceEntry` is a single entry of the + ## stack trace. + procname*: cstring ## Name of the proc that is currently executing. + line*: int ## Line number of the proc that is currently executing. + filename*: cstring ## Filename of the proc that is currently executing. + when NimStackTraceMsgs: + frameMsg*: string ## When a stacktrace is generated in a given frame and + ## rendered at a later time, we should ensure the stacktrace + ## data isn't invalidated; any pointer into PFrame is + ## subject to being invalidated so shouldn't be stored. + when defined(nimStackTraceOverride): + programCounter*: uint ## Program counter - will be used to get the rest of the info, + ## when `$` is called on this type. We can't use + ## "cuintptr_t" in here. + procnameStr*, filenameStr*: string ## GC-ed alternatives to "procname" and "filename" + + Exception* {.compilerproc, magic: "Exception".} = object of RootObj ## \ + ## Base exception class. + ## + ## Each exception has to inherit from `Exception`. See the full `exception + ## hierarchy `_. + parent*: ref Exception ## Parent exception (can be used as a stack). + name*: cstring ## The exception's name is its Nim identifier. + ## This field is filled automatically in the + ## `raise` statement. + msg* {.exportc: "message".}: string ## The exception's message. Not + ## providing an exception message + ## is bad style. + when defined(js): + trace: string + else: + trace: seq[StackTraceEntry] + up: ref Exception # used for stacking exceptions. Not exported! + + Defect* = object of Exception ## \ + ## Abstract base class for all exceptions that Nim's runtime raises + ## but that are strictly uncatchable as they can also be mapped to + ## a `quit` / `trap` / `exit` operation. + + CatchableError* = object of Exception ## \ + ## Abstract class for all exceptions that are catchable. -include "system/exceptions" +when defined(nimIcIntegrityChecks): + include "system/exceptions" +else: + import system/exceptions + export exceptions when defined(js) or defined(nimdoc): type @@ -624,10 +549,10 @@ proc sizeof*[T](x: T): int {.magic: "SizeOf", noSideEffect.} ## sizeof should fallback to the `sizeof` in the C compiler. The ## result isn't available for the Nim compiler and therefore can't ## be used inside of macros. - ## - ## .. code-block:: Nim - ## sizeof('A') # => 1 - ## sizeof(2) # => 8 + ## ``` + ## sizeof('A') # => 1 + ## sizeof(2) # => 8 + ## ``` proc alignof*[T](x: T): int {.magic: "AlignOf", noSideEffect.} proc alignof*(x: typedesc): int {.magic: "AlignOf", noSideEffect.} @@ -655,8 +580,7 @@ proc newSeq*[T](s: var seq[T], len: Natural) {.magic: "NewSeq", noSideEffect.} ## Note that the sequence will be filled with zeroed entries. ## After the creation of the sequence you should assign entries to ## the sequence instead of adding them. Example: - ## - ## .. code-block:: Nim + ## ``` ## var inputStrings: seq[string] ## newSeq(inputStrings, 3) ## assert len(inputStrings) == 3 @@ -664,6 +588,7 @@ proc newSeq*[T](s: var seq[T], len: Natural) {.magic: "NewSeq", noSideEffect.} ## inputStrings[1] = "assignment" ## inputStrings[2] = "would crash" ## #inputStrings[3] = "out of bounds" + ## ``` proc newSeq*[T](len = 0.Natural): seq[T] = ## Creates a new sequence of type `seq[T]` with length `len`. @@ -671,30 +596,30 @@ proc newSeq*[T](len = 0.Natural): seq[T] = ## Note that the sequence will be filled with zeroed entries. ## After the creation of the sequence you should assign entries to ## the sequence instead of adding them. - ## - ## See also: - ## * `newSeqOfCap <#newSeqOfCap,Natural>`_ - ## * `newSeqUninitialized <#newSeqUninitialized,Natural>`_ - ## - ## .. code-block:: Nim + ## ``` ## var inputStrings = newSeq[string](3) ## assert len(inputStrings) == 3 ## inputStrings[0] = "The fourth" ## inputStrings[1] = "assignment" ## inputStrings[2] = "would crash" ## #inputStrings[3] = "out of bounds" + ## ``` + ## + ## See also: + ## * `newSeqOfCap <#newSeqOfCap,Natural>`_ + ## * `newSeqUninitialized <#newSeqUninitialized,Natural>`_ newSeq(result, len) proc newSeqOfCap*[T](cap: Natural): seq[T] {. magic: "NewSeqOfCap", noSideEffect.} = ## Creates a new sequence of type `seq[T]` with length zero and capacity - ## `cap`. - ## - ## .. code-block:: Nim + ## `cap`. Example: + ## ``` ## var x = newSeqOfCap[int](5) ## assert len(x) == 0 ## x.add(10) ## assert len(x) == 1 + ## ``` discard when not defined(js): @@ -704,11 +629,12 @@ when not defined(js): ## Only available for numbers types. Note that the sequence will be ## uninitialized. After the creation of the sequence you should assign ## entries to the sequence instead of adding them. - ## - ## .. code-block:: Nim + ## Example: + ## ``` ## var x = newSeqUninitialized[int](3) ## assert len(x) == 3 ## x[0] = 10 + ## ``` result = newSeqOfCap[T](len) when defined(nimSeqsV2): cast[ptr int](addr result)[] = len @@ -791,62 +717,39 @@ func chr*(u: range[0..255]): char {.magic: "Chr".} = doAssertRaises(RangeDefect): discard chr(x) doAssertRaises(RangeDefect): discard char(x) -# floating point operations: -proc `+`*(x: float32): float32 {.magic: "UnaryPlusF64", noSideEffect.} -proc `-`*(x: float32): float32 {.magic: "UnaryMinusF64", noSideEffect.} -proc `+`*(x, y: float32): float32 {.magic: "AddF64", noSideEffect.} -proc `-`*(x, y: float32): float32 {.magic: "SubF64", noSideEffect.} -proc `*`*(x, y: float32): float32 {.magic: "MulF64", noSideEffect.} -proc `/`*(x, y: float32): float32 {.magic: "DivF64", noSideEffect.} - -proc `+`*(x: float): float {.magic: "UnaryPlusF64", noSideEffect.} -proc `-`*(x: float): float {.magic: "UnaryMinusF64", noSideEffect.} -proc `+`*(x, y: float): float {.magic: "AddF64", noSideEffect.} -proc `-`*(x, y: float): float {.magic: "SubF64", noSideEffect.} -proc `*`*(x, y: float): float {.magic: "MulF64", noSideEffect.} -proc `/`*(x, y: float): float {.magic: "DivF64", noSideEffect.} - -proc `==`*(x, y: float32): bool {.magic: "EqF64", noSideEffect.} -proc `<=`*(x, y: float32): bool {.magic: "LeF64", noSideEffect.} -proc `<` *(x, y: float32): bool {.magic: "LtF64", noSideEffect.} - -proc `==`*(x, y: float): bool {.magic: "EqF64", noSideEffect.} -proc `<=`*(x, y: float): bool {.magic: "LeF64", noSideEffect.} -proc `<`*(x, y: float): bool {.magic: "LtF64", noSideEffect.} - include "system/setops" proc contains*[U, V, W](s: HSlice[U, V], value: W): bool {.noSideEffect, inline.} = ## Checks if `value` is within the range of `s`; returns true if - ## `value >= s.a and value <= s.b` - ## - ## .. code-block:: Nim + ## `value >= s.a and value <= s.b`. + ## ``` ## assert((1..3).contains(1) == true) ## assert((1..3).contains(2) == true) ## assert((1..3).contains(4) == false) + ## ``` result = s.a <= value and value <= s.b template `in`*(x, y: untyped): untyped {.dirty.} = contains(y, x) ## Sugar for `contains`. - ## - ## .. code-block:: Nim + ## ``` ## assert(1 in (1..3) == true) ## assert(5 in (1..3) == false) + ## ``` template `notin`*(x, y: untyped): untyped {.dirty.} = not contains(y, x) ## Sugar for `not contains`. - ## - ## .. code-block:: Nim + ## ``` ## assert(1 notin (1..3) == false) ## assert(5 notin (1..3) == true) + ## ``` proc `is`*[T, S](x: T, y: S): bool {.magic: "Is", noSideEffect.} ## Checks if `T` is of the same type as `S`. ## ## For a negated version, use `isnot <#isnot.t,untyped,untyped>`_. ## - ## .. code-block:: Nim + ## ``` ## assert 42 is int ## assert @[1, 2] is seq ## @@ -858,12 +761,13 @@ proc `is`*[T, S](x: T, y: S): bool {.magic: "Is", noSideEffect.} ## ## assert(test[int](3) == 3) ## assert(test[string]("xyz") == 0) + ## ``` template `isnot`*(x, y: untyped): untyped = not (x is y) ## Negated version of `is <#is,T,S>`_. Equivalent to `not(x is y)`. - ## - ## .. code-block:: Nim + ## ``` ## assert 42 isnot float ## assert @[1, 2] isnot enum + ## ``` when (defined(nimOwnedEnabled) and not defined(nimscript)) or defined(nimFixedOwned): type owned*[T]{.magic: "BuiltinType".} ## type constructor to mark a ref/ptr or a closure as `owned`. @@ -956,10 +860,10 @@ proc cmp*[T](x, y: T): int = ## ## This is useful for writing generic algorithms without performance loss. ## This generic implementation uses the `==` and `<` operators. - ## - ## .. code-block:: Nim - ## import std/algorithm - ## echo sorted(@[4, 2, 6, 5, 8, 7], cmp[int]) + ## ``` + ## import std/algorithm + ## echo sorted(@[4, 2, 6, 5, 8, 7], cmp[int]) + ## ``` if x == y: return 0 if x < y: return -1 return 1 @@ -977,13 +881,14 @@ proc `@`* [IDX, T](a: sink array[IDX, T]): seq[T] {.magic: "ArrToSeq", noSideEff ## sequences with the array constructor: `@[1, 2, 3]` has the type ## `seq[int]`, while `[1, 2, 3]` has the type `array[0..2, int]`. ## - ## .. code-block:: Nim + ## ``` ## let ## a = [1, 3, 5] ## b = "foo" ## ## echo @a # => @[1, 3, 5] ## echo @b # => @['f', 'o', 'o'] + ## ``` proc default*[T](_: typedesc[T]): T {.magic: "Default", noSideEffect.} = ## returns the default value of the type `T`. @@ -1013,14 +918,14 @@ proc setLen*[T](s: var seq[T], newlen: Natural) {. ## ## If the current length is greater than the new length, ## `s` will be truncated. - ## - ## .. code-block:: Nim + ## ``` ## var x = @[10, 20] ## x.setLen(5) ## x[4] = 50 ## assert x == @[10, 20, 0, 0, 50] ## x.setLen(1) ## assert x == @[10] + ## ``` proc setLen*(s: var string, newlen: Natural) {. magic: "SetLengthStr", noSideEffect.} @@ -1028,11 +933,11 @@ proc setLen*(s: var string, newlen: Natural) {. ## ## If the current length is greater than the new length, ## `s` will be truncated. - ## - ## .. code-block:: Nim - ## var myS = "Nim is great!!" - ## myS.setLen(3) # myS <- "Nim" - ## echo myS, " is fantastic!!" + ## ``` + ## var myS = "Nim is great!!" + ## myS.setLen(3) # myS <- "Nim" + ## echo myS, " is fantastic!!" + ## ``` proc newString*(len: Natural): string {. magic: "NewString", importc: "mnewString", noSideEffect.} @@ -1051,41 +956,41 @@ proc newStringOfCap*(cap: Natural): string {. ## be achieved with the `&` operator or with `add`. proc `&`*(x: string, y: char): string {. - magic: "ConStrStr", noSideEffect, merge.} + magic: "ConStrStr", noSideEffect.} ## Concatenates `x` with `y`. - ## - ## .. code-block:: Nim + ## ``` ## assert("ab" & 'c' == "abc") + ## ``` proc `&`*(x, y: char): string {. - magic: "ConStrStr", noSideEffect, merge.} + magic: "ConStrStr", noSideEffect.} ## Concatenates characters `x` and `y` into a string. - ## - ## .. code-block:: Nim + ## ``` ## assert('a' & 'b' == "ab") + ## ``` proc `&`*(x, y: string): string {. - magic: "ConStrStr", noSideEffect, merge.} + magic: "ConStrStr", noSideEffect.} ## Concatenates strings `x` and `y`. - ## - ## .. code-block:: Nim + ## ``` ## assert("ab" & "cd" == "abcd") + ## ``` proc `&`*(x: char, y: string): string {. - magic: "ConStrStr", noSideEffect, merge.} + magic: "ConStrStr", noSideEffect.} ## Concatenates `x` with `y`. - ## - ## .. code-block:: Nim + ## ``` ## assert('a' & "bc" == "abc") + ## ``` # implementation note: These must all have the same magic value "ConStrStr" so # that the merge optimization works properly. proc add*(x: var string, y: char) {.magic: "AppendStrCh", noSideEffect.} ## Appends `y` to `x` in place. - ## - ## .. code-block:: Nim + ## ``` ## var tmp = "" ## tmp.add('a') ## tmp.add('b') ## assert(tmp == "ab") + ## ``` proc add*(x: var string, y: string) {.magic: "AppendStrStr", noSideEffect.} = ## Concatenates `x` and `y` in place. @@ -1102,18 +1007,6 @@ type littleEndian, bigEndian const - isMainModule* {.magic: "IsMainModule".}: bool = false - ## True only when accessed in the main module. This works thanks to - ## compiler magic. It is useful to embed testing code in a module. - - CompileDate* {.magic: "CompileDate".}: string = "0000-00-00" - ## 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" - ## 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 ## The endianness of the target CPU. This is a valuable piece of ## information for low-level code only. This works thanks to compiler @@ -1163,7 +1056,8 @@ when defined(boehmgc): const boehmLib = "libgc.so.1" {.pragma: boehmGC, noconv, dynlib: boehmLib.} -type TaintedString* {.deprecated: "Deprecated since 1.5".} = string +when not defined(nimPreviewSlimSystem): + type TaintedString* {.deprecated: "Deprecated since 1.5".} = string when defined(profiler) and not defined(nimscript): @@ -1278,13 +1172,13 @@ when false: # defined(gcDestructors): ## containers should also call their adding proc `add` for consistency. ## Generic code becomes much easier to write if the Nim naming scheme is ## respected. + ## ``` + ## var s: seq[string] = @["test2","test2"] + ## s.add("test") # s <- @[test2, test2, test] + ## ``` ## ## See also: ## * `& proc <#&,seq[T],seq[T]>`_ - ## - ## .. code-block:: Nim - ## var s: seq[string] = @["test2","test2"] - ## s.add("test") # s <- @[test2, test2, test] {.noSideEffect.}: let xl = x.len setLen(x, xl + y.len) @@ -1303,13 +1197,13 @@ else: ## containers should also call their adding proc `add` for consistency. ## Generic code becomes much easier to write if the Nim naming scheme is ## respected. + ## ``` + ## var s: seq[string] = @["test2","test2"] + ## s.add("test") # s <- @[test2, test2, test] + ## ``` ## ## See also: ## * `& proc <#&,seq[T],seq[T]>`_ - ## - ## .. code-block:: Nim - ## var s: seq[string] = @["test2","test2"] - ## s.add("test") # s <- @[test2, test2, test] {.noSideEffect.}: let xl = x.len setLen(x, xl + y.len) @@ -1340,10 +1234,10 @@ proc del*[T](x: var seq[T], i: Natural) {.noSideEffect.} = proc insert*[T](x: var seq[T], item: sink T, i = 0.Natural) {.noSideEffect.} = ## Inserts `item` into `x` at position `i`. - ## - ## .. code-block:: Nim - ## var i = @[1, 3, 5] - ## i.insert(99, 0) # i <- @[99, 1, 3, 5] + ## ``` + ## var i = @[1, 3, 5] + ## i.insert(99, 0) # i <- @[99, 1, 3, 5] + ## ``` {.noSideEffect.}: template defaultImpl = let xl = x.len @@ -1371,89 +1265,35 @@ when not defined(nimV2): ## ## It works even for complex data graphs with cycles. This is a great ## debugging tool. - ## - ## .. code-block:: Nim - ## var s: seq[string] = @["test2", "test2"] - ## var i = @[1, 2, 3, 4, 5] - ## echo repr(s) # => 0x1055eb050[0x1055ec050"test2", 0x1055ec078"test2"] - ## echo repr(i) # => 0x1055ed050[1, 2, 3, 4, 5] + ## ``` + ## var s: seq[string] = @["test2", "test2"] + ## var i = @[1, 2, 3, 4, 5] + ## echo repr(s) # => 0x1055eb050[0x1055ec050"test2", 0x1055ec078"test2"] + ## echo repr(i) # => 0x1055ed050[1, 2, 3, 4, 5] + ## ``` -type - ByteAddress* = int - ## is the signed integer type that should be used for converting - ## pointers to integer addresses for readability. +import system/ctypes +export ctypes - BiggestFloat* = float64 - ## is an alias for the biggest floating point type the Nim - ## compiler supports. Currently this is `float64`, but it is - ## platform-dependent in general. +when not defined(nimPreviewSlimSystem): + type + csize* {.importc: "size_t", nodecl, deprecated: "use `csize_t` instead".} = int + ## This isn't the same as `size_t` in *C*. Don't use it. -when defined(js): - type BiggestUInt* = uint32 - ## is an alias for the biggest unsigned integer type the Nim compiler - ## supports. Currently this is `uint32` for JS and `uint64` for other - ## targets. -else: - type BiggestUInt* = uint64 - ## is an alias for the biggest unsigned integer type the Nim compiler - ## supports. Currently this is `uint32` for JS and `uint64` for other - ## targets. +const + Inf* = 0x7FF0000000000000'f64 + ## Contains the IEEE floating point value of positive infinity. + NegInf* = 0xFFF0000000000000'f64 + ## Contains the IEEE floating point value of negative infinity. + NaN* = 0x7FF7FFFFFFFFFFFF'f64 + ## Contains an IEEE floating point value of *Not A Number*. + ## + ## Note that you cannot compare a floating point value to this value + ## and expect a reasonable result - use the `isNaN` or `classify` procedure + ## in the `math module `_ for checking for NaN. -when defined(windows): - type - clong* {.importc: "long", nodecl.} = int32 - ## This is the same as the type `long` in *C*. - culong* {.importc: "unsigned long", nodecl.} = uint32 - ## This is the same as the type `unsigned long` in *C*. -else: - type - clong* {.importc: "long", nodecl.} = int - ## This is the same as the type `long` in *C*. - culong* {.importc: "unsigned long", nodecl.} = uint - ## This is the same as the type `unsigned long` in *C*. - -type # these work for most platforms: - cchar* {.importc: "char", nodecl.} = char - ## This is the same as the type `char` in *C*. - cschar* {.importc: "signed char", nodecl.} = int8 - ## This is the same as the type `signed char` in *C*. - cshort* {.importc: "short", nodecl.} = int16 - ## This is the same as the type `short` in *C*. - cint* {.importc: "int", nodecl.} = int32 - ## This is the same as the type `int` in *C*. - csize* {.importc: "size_t", nodecl, deprecated: "use `csize_t` instead".} = int - ## This isn't the same as `size_t` in *C*. Don't use it. - csize_t* {.importc: "size_t", nodecl.} = uint - ## This is the same as the type `size_t` in *C*. - clonglong* {.importc: "long long", nodecl.} = int64 - ## This is the same as the type `long long` in *C*. - cfloat* {.importc: "float", nodecl.} = float32 - ## This is the same as the type `float` in *C*. - cdouble* {.importc: "double", nodecl.} = float64 - ## This is the same as the type `double` in *C*. - clongdouble* {.importc: "long double", nodecl.} = BiggestFloat - ## This is the same as the type `long double` in *C*. - ## This C type is not supported by Nim's code generator. - - cuchar* {.importc: "unsigned char", nodecl, deprecated: "use `char` or `uint8` instead".} = char - ## Deprecated: Use `uint8` instead. - cushort* {.importc: "unsigned short", nodecl.} = uint16 - ## This is the same as the type `unsigned short` in *C*. - cuint* {.importc: "unsigned int", nodecl.} = uint32 - ## This is the same as the type `unsigned int` in *C*. - culonglong* {.importc: "unsigned long long", nodecl.} = uint64 - ## This is the same as the type `unsigned long long` in *C*. - - cstringArray* {.importc: "char**", nodecl.} = ptr UncheckedArray[cstring] - ## This is binary compatible to the type `char**` in *C*. The array's - ## high value is large enough to disable bounds checking in practice. - ## Use `cstringArrayToSeq proc <#cstringArrayToSeq,cstringArray,Natural>`_ - ## to convert it into a `seq[string]`. - - PFloat32* = ptr float32 ## An alias for `ptr float32`. - PFloat64* = ptr float64 ## An alias for `ptr float64`. - PInt64* = ptr int64 ## An alias for `ptr int64`. - PInt32* = ptr int32 ## An alias for `ptr int32`. +proc high*(T: typedesc[SomeFloat]): T = Inf +proc low*(T: typedesc[SomeFloat]): T = NegInf proc toFloat*(i: int): float {.noSideEffect, inline.} = ## Converts an integer `i` into a `float`. Same as `float(i)`. @@ -1461,12 +1301,13 @@ proc toFloat*(i: int): float {.noSideEffect, inline.} = ## If the conversion fails, `ValueError` is raised. ## However, on most platforms the conversion cannot fail. ## - ## .. code-block:: Nim + ## ``` ## let ## a = 2 ## b = 3.7 ## ## echo a.toFloat + b # => 5.7 + ## ``` float(i) proc toBiggestFloat*(i: BiggestInt): BiggestFloat {.noSideEffect, inline.} = @@ -1483,29 +1324,79 @@ proc toInt*(f: float): int {.noSideEffect.} = ## ## Note that some floating point numbers (e.g. infinity or even 1e19) ## cannot be accurately converted. - ## - ## .. code-block:: Nim + ## ``` ## doAssert toInt(0.49) == 0 ## doAssert toInt(0.5) == 1 ## doAssert toInt(-0.5) == -1 # rounding is symmetrical + ## ``` if f >= 0: int(f+0.5) else: int(f-0.5) proc toBiggestInt*(f: BiggestFloat): BiggestInt {.noSideEffect.} = ## Same as `toInt <#toInt,float>`_ but for `BiggestFloat` to `BiggestInt`. if f >= 0: BiggestInt(f+0.5) else: BiggestInt(f-0.5) -proc addQuitProc*(quitProc: proc() {.noconv.}) {. - importc: "atexit", header: "", deprecated: "use exitprocs.addExitProc".} - ## Adds/registers a quit procedure. +proc `/`*(x, y: int): float {.inline, noSideEffect.} = + ## Division of integers that results in a float. + ## ``` + ## echo 7 / 5 # => 1.4 + ## ``` ## - ## Each call to `addQuitProc` registers another quit procedure. Up to 30 - ## procedures can be registered. They are executed on a last-in, first-out - ## basis (that is, the last function registered is the first to be executed). - ## `addQuitProc` raises an EOutOfIndex exception if `quitProc` cannot be - ## registered. - # Support for addQuitProc() is done by Ansi C's facilities here. - # In case of an unhandled exception the exit handlers should - # not be called explicitly! The user may decide to do this manually though. + ## See also: + ## * `div `_ + ## * `mod `_ + result = toFloat(x) / toFloat(y) + +{.push stackTrace: off.} + +when defined(js): + proc js_abs[T: SomeNumber](x: T): T {.importc: "Math.abs".} +else: + proc c_fabs(x: cdouble): cdouble {.importc: "fabs", header: "".} + proc c_fabsf(x: cfloat): cfloat {.importc: "fabsf", header: "".} + +proc abs*[T: float64 | float32](x: T): T {.noSideEffect, inline.} = + when nimvm: + if x < 0.0: result = -x + elif x == 0.0: result = 0.0 # handle 0.0, -0.0 + else: result = x # handle NaN, > 0 + else: + when defined(js): result = js_abs(x) + else: + when T is float64: + result = c_fabs(x) + else: + result = c_fabsf(x) + +func abs*(x: int): int {.magic: "AbsI", inline.} = + if x < 0: -x else: x +func abs*(x: int8): int8 {.magic: "AbsI", inline.} = + if x < 0: -x else: x +func abs*(x: int16): int16 {.magic: "AbsI", inline.} = + if x < 0: -x else: x +func abs*(x: int32): int32 {.magic: "AbsI", inline.} = + if x < 0: -x else: x +func abs*(x: int64): int64 {.magic: "AbsI", inline.} = + ## 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). + result = if x < 0: -x else: x + +{.pop.} # stackTrace: off + +when not defined(nimPreviewSlimSystem): + proc addQuitProc*(quitProc: proc() {.noconv.}) {. + importc: "atexit", header: "", deprecated: "use exitprocs.addExitProc".} + ## Adds/registers a quit procedure. + ## + ## Each call to `addQuitProc` registers another quit procedure. Up to 30 + ## procedures can be registered. They are executed on a last-in, first-out + ## basis (that is, the last function registered is the first to be executed). + ## `addQuitProc` raises an EOutOfIndex exception if `quitProc` cannot be + ## registered. + # Support for addQuitProc() is done by Ansi C's facilities here. + # In case of an unhandled exception the exit handlers should + # not be called explicitly! The user may decide to do this manually though. proc swap*[T](a, b: var T) {.magic: "Swap", noSideEffect.} ## Swaps the values `a` and `b`. @@ -1513,7 +1404,7 @@ proc swap*[T](a, b: var T) {.magic: "Swap", noSideEffect.} ## This is often more efficient than `tmp = a; a = b; b = tmp`. ## Particularly useful for sorting algorithms. ## - ## .. code-block:: Nim + ## ``` ## var ## a = 5 ## b = 9 @@ -1522,6 +1413,7 @@ proc swap*[T](a, b: var T) {.magic: "Swap", noSideEffect.} ## ## assert a == 9 ## assert b == 5 + ## ``` when not defined(js) and not defined(booting) and defined(nimTrMacros): template swapRefsInArray*{swap(arr[a], arr[b])}(arr: openArray[ref], a, b: int) = @@ -1530,18 +1422,6 @@ when not defined(js) and not defined(booting) and defined(nimTrMacros): # unnecessary slow down in this case. swap(cast[ptr pointer](addr arr[a])[], cast[ptr pointer](addr arr[b])[]) -const - Inf* = 0x7FF0000000000000'f64 - ## Contains the IEEE floating point value of positive infinity. - NegInf* = 0xFFF0000000000000'f64 - ## Contains the IEEE floating point value of negative infinity. - NaN* = 0x7FF7FFFFFFFFFFFF'f64 - ## Contains an IEEE floating point value of *Not A Number*. - ## - ## Note that you cannot compare a floating point value to this value - ## and expect a reasonable result - use the `isNaN` or `classify` procedure - ## in the `math module `_ for checking for NaN. - include "system/memalloc" @@ -1551,67 +1431,14 @@ proc `|`*(a, b: typedesc): typedesc = discard include "system/iterators_1" -{.push stackTrace: off.} - - -when defined(js): - proc js_abs[T: SomeNumber](x: T): T {.importc: "Math.abs".} -else: - proc c_fabs(x: cdouble): cdouble {.importc: "fabs", header: "".} - proc c_fabsf(x: cfloat): cfloat {.importc: "fabsf", header: "".} - -proc abs*[T: float64 | float32](x: T): T {.noSideEffect, inline.} = - when nimvm: - if x < 0.0: result = -x - elif x == 0.0: result = 0.0 # handle 0.0, -0.0 - else: result = x # handle NaN, > 0 - else: - when defined(js): result = js_abs(x) - else: - when T is float64: - result = c_fabs(x) - else: - result = c_fabsf(x) - -proc min*(x, y: float32): float32 {.noSideEffect, inline.} = - if x <= y or y != y: x else: y -proc min*(x, y: float64): float64 {.noSideEffect, inline.} = - if x <= y or y != y: x else: y -proc max*(x, y: float32): float32 {.noSideEffect, inline.} = - if y <= x or y != y: x else: y -proc max*(x, y: float64): float64 {.noSideEffect, inline.} = - if y <= x or y != y: x else: y -proc min*[T: not SomeFloat](x, y: T): T {.inline.} = - if x <= y: x else: y -proc max*[T: not SomeFloat](x, y: T): T {.inline.} = - if y <= x: x else: y - -{.pop.} # stackTrace: off - - -proc high*(T: typedesc[SomeFloat]): T = Inf -proc low*(T: typedesc[SomeFloat]): T = NegInf - proc len*[U: Ordinal; V: Ordinal](x: HSlice[U, V]): int {.noSideEffect, inline.} = ## Length of ordinal slice. When x.b < x.a returns zero length. - ## - ## .. code-block:: Nim + ## ``` ## assert((0..5).len == 6) ## assert((5..2).len == 0) + ## ``` result = max(0, ord(x.b) - ord(x.a) + 1) -when true: # PRTEMP: remove? - proc isNil*[T](x: seq[T]): bool {.noSideEffect, magic: "IsNil", error.} - ## Seqs are no longer nil by default, but set and empty. - ## Check for zero length instead. - ## - ## See also: - ## * `isNil(string) <#isNil,string>`_ - - proc isNil*(x: string): bool {.noSideEffect, magic: "IsNil", error.} - ## See also: - ## * `isNil(seq[T]) <#isNil,seq[T]>`_ - proc isNil*[T](x: ref T): bool {.noSideEffect, magic: "IsNil".} proc isNil*[T](x: ptr T): bool {.noSideEffect, magic: "IsNil".} @@ -1622,13 +1449,23 @@ proc isNil*[T: proc](x: T): bool {.noSideEffect, magic: "IsNil".} ## `== nil`. -proc `@`*[T](a: openArray[T]): seq[T] = - ## Turns an *openArray* into a sequence. - ## - ## This is not as efficient as turning a fixed length array into a sequence - ## as it always copies every element of `a`. - newSeq(result, a.len) - for i in 0..a.len-1: result[i] = a[i] +when defined(nimHasTopDownInference): + # magic used for seq type inference + proc `@`*[T](a: openArray[T]): seq[T] {.magic: "OpenArrayToSeq".} = + ## Turns an *openArray* into a sequence. + ## + ## This is not as efficient as turning a fixed length array into a sequence + ## as it always copies every element of `a`. + newSeq(result, a.len) + for i in 0..a.len-1: result[i] = a[i] +else: + proc `@`*[T](a: openArray[T]): seq[T] = + ## Turns an *openArray* into a sequence. + ## + ## This is not as efficient as turning a fixed length array into a sequence + ## as it always copies every element of `a`. + newSeq(result, a.len) + for i in 0..a.len-1: result[i] = a[i] when defined(nimSeqsV2): @@ -1637,12 +1474,12 @@ when defined(nimSeqsV2): ## Concatenates two sequences. ## ## Requires copying of the sequences. + ## ``` + ## assert(@[1, 2, 3, 4] & @[5, 6] == @[1, 2, 3, 4, 5, 6]) + ## ``` ## ## See also: ## * `add(var seq[T], openArray[T]) <#add,seq[T],openArray[T]>`_ - ## - ## .. code-block:: Nim - ## assert(@[1, 2, 3, 4] & @[5, 6] == @[1, 2, 3, 4, 5, 6]) newSeq(result, x.len + y.len) for i in 0..x.len-1: result[i] = move(x[i]) @@ -1653,12 +1490,12 @@ when defined(nimSeqsV2): ## Appends element y to the end of the sequence. ## ## Requires copying of the sequence. + ## ``` + ## assert(@[1, 2, 3] & 4 == @[1, 2, 3, 4]) + ## ``` ## ## See also: ## * `add(var seq[T], T) <#add,seq[T],sinkT>`_ - ## - ## .. code-block:: Nim - ## assert(@[1, 2, 3] & 4 == @[1, 2, 3, 4]) newSeq(result, x.len + 1) for i in 0..x.len-1: result[i] = move(x[i]) @@ -1668,9 +1505,9 @@ when defined(nimSeqsV2): ## Prepends the element x to the beginning of the sequence. ## ## Requires copying of the sequence. - ## - ## .. code-block:: Nim + ## ``` ## assert(1 & @[2, 3, 4] == @[1, 2, 3, 4]) + ## ``` newSeq(result, y.len + 1) result[0] = move(x) for i in 0..y.len-1: @@ -1682,12 +1519,12 @@ else: ## Concatenates two sequences. ## ## Requires copying of the sequences. + ## ``` + ## assert(@[1, 2, 3, 4] & @[5, 6] == @[1, 2, 3, 4, 5, 6]) + ## ``` ## ## See also: ## * `add(var seq[T], openArray[T]) <#add,seq[T],openArray[T]>`_ - ## - ## .. code-block:: Nim - ## assert(@[1, 2, 3, 4] & @[5, 6] == @[1, 2, 3, 4, 5, 6]) newSeq(result, x.len + y.len) for i in 0..x.len-1: result[i] = x[i] @@ -1698,12 +1535,12 @@ else: ## Appends element y to the end of the sequence. ## ## Requires copying of the sequence. + ## ``` + ## assert(@[1, 2, 3] & 4 == @[1, 2, 3, 4]) + ## ``` ## ## See also: ## * `add(var seq[T], T) <#add,seq[T],sinkT>`_ - ## - ## .. code-block:: Nim - ## assert(@[1, 2, 3] & 4 == @[1, 2, 3, 4]) newSeq(result, x.len + 1) for i in 0..x.len-1: result[i] = x[i] @@ -1713,19 +1550,15 @@ else: ## Prepends the element x to the beginning of the sequence. ## ## Requires copying of the sequence. - ## - ## .. code-block:: Nim + ## ``` ## assert(1 & @[2, 3, 4] == @[1, 2, 3, 4]) + ## ``` newSeq(result, y.len + 1) result[0] = x for i in 0..y.len-1: result[i+1] = y[i] -proc astToStr*[T](x: T): string {.magic: "AstToStr", noSideEffect.} - ## Converts the AST of `x` into a string representation. This is very useful - ## for debugging. - proc instantiationInfo*(index = -1, fullPaths = false): tuple[ filename: string, line: int, column: int] {.magic: "InstantiationInfo", noSideEffect.} ## Provides access to the compiler's instantiation stack line information @@ -1738,7 +1571,7 @@ proc instantiationInfo*(index = -1, fullPaths = false): tuple[ ## to retrieve information about the current filename and line number. ## Example: ## - ## .. code-block:: nim + ## ``` ## import std/strutils ## ## template testException(exception, code: untyped): typed = @@ -1760,16 +1593,8 @@ proc instantiationInfo*(index = -1, fullPaths = false): tuple[ ## testException(IndexDefect, tester(30)) ## testException(IndexDefect, tester(1)) ## # --> Test failure at example.nim:20 with 'tester(1)' + ## ``` -proc compiles*(x: untyped): bool {.magic: "Compiles", noSideEffect, compileTime.} = - ## Special compile-time procedure that checks whether `x` can be compiled - ## without any semantic error. - ## This can be used to check whether a type supports some operation: - ## - ## .. code-block:: Nim - ## when compiles(3 + 4): - ## echo "'+' for integers is available" - discard when notJSnotNims: import system/ansi_c @@ -1831,8 +1656,7 @@ when defined(nimV2): include system/arc when not defined(nimPreviewSlimSystem): - {.deprecated: """assertions is about to move out of system; use `-d:nimPreviewSlimSystem` and - import `std/assertions`.""".} + {.deprecated: "assertions is about to move out of system; use `-d:nimPreviewSlimSystem` and import `std/assertions`".} import std/assertions export assertions @@ -1855,12 +1679,12 @@ proc contains*[T](a: openArray[T], item: T): bool {.inline.}= ## ## This allows the `in` operator: `a.contains(item)` is the same as ## `item in a`. - ## - ## .. code-block:: Nim + ## ``` ## var a = @[1, 3, 5] ## assert a.contains(5) ## assert 3 in a ## assert 99 notin a + ## ``` return find(a, item) >= 0 proc pop*[T](s: var seq[T]): T {.inline, noSideEffect.} = @@ -1947,8 +1771,7 @@ when notJSnotNims: ## ## `outOfMemHook` can be used to raise an exception in case of OOM like so: ## - ## .. code-block:: Nim - ## + ## ``` ## var gOutOfMem: ref EOutOfMemory ## new(gOutOfMem) # need to be allocated *before* OOM really happened! ## gOutOfMem.msg = "out of memory" @@ -1957,6 +1780,7 @@ when notJSnotNims: ## raise gOutOfMem ## ## system.outOfMemHook = handleOOM + ## ``` ## ## If the handler does not raise an exception, ordinary control flow ## continues and the program is terminated. @@ -2055,22 +1879,6 @@ proc getTypeInfo*[T](x: T): pointer {.magic: "GetTypeInfo", benign.} ## Ordinary code should not use this, but the `typeinfo module ## `_ instead. -{.push stackTrace: off.} -func abs*(x: int): int {.magic: "AbsI", inline.} = - if x < 0: -x else: x -func abs*(x: int8): int8 {.magic: "AbsI", inline.} = - if x < 0: -x else: x -func abs*(x: int16): int16 {.magic: "AbsI", inline.} = - if x < 0: -x else: x -func abs*(x: int32): int32 {.magic: "AbsI", inline.} = - if x < 0: -x else: x -func abs*(x: int64): int64 {.magic: "AbsI", inline.} = - ## 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). - result = if x < 0: -x else: x -{.pop.} when not defined(js): @@ -2083,13 +1891,13 @@ template likely*(val: bool): bool = ## You can use this template to decorate a branch condition. On certain ## platforms this can help the processor predict better which branch is ## going to be run. Example: - ## - ## .. code-block:: Nim + ## ``` ## for value in inputValues: ## if likely(value <= 100): ## process(value) ## else: ## echo "Value too big!" + ## ``` ## ## On backends without branch prediction (JS and the nimscript VM), this ## template will not affect code execution. @@ -2107,13 +1915,13 @@ template unlikely*(val: bool): bool = ## You can use this proc to decorate a branch condition. On certain ## platforms this can help the processor predict better which branch is ## going to be run. Example: - ## - ## .. code-block:: Nim + ## ``` ## for value in inputValues: ## if unlikely(value > 100): ## echo "Value too big!" ## else: ## process(value) + ## ``` ## ## On backends without branch prediction (JS and the nimscript VM), this ## template will not affect code execution. @@ -2125,22 +1933,6 @@ template unlikely*(val: bool): bool = else: unlikelyProc(val) -const - NimMajor* {.intdefine.}: int = 1 - ## is the major number of Nim's version. Example: - ## - ## .. code-block:: Nim - ## when (NimMajor, NimMinor, NimPatch) >= (1, 3, 1): discard - # see also std/private/since - - NimMinor* {.intdefine.}: int = 7 - ## is the minor number of Nim's version. - ## Odd for devel, even for releases. - - NimPatch* {.intdefine.}: int = 1 - ## is the patch number of Nim's version. - ## Odd for devel, even for releases. - import system/dollars export dollars @@ -2189,16 +1981,6 @@ const NimVersion*: string = $NimMajor & "." & $NimMinor & "." & $NimPatch ## is the version of Nim as a string. - -type - FileSeekPos* = enum ## Position relative to which seek should happen. - # The values are ordered so that they match with stdio - # SEEK_SET, SEEK_CUR and SEEK_END respectively. - fspSet ## Seek to absolute value - fspCur ## Seek relative to current position - fspEnd ## Seek relative to end - - when not defined(js): {.push stackTrace: off, profiler: off.} @@ -2324,13 +2106,14 @@ when notJSnotNims: ## is pressed. Only one such hook is supported. ## Example: ## - ## .. code-block:: Nim + ## ``` ## proc ctrlc() {.noconv.} = ## echo "Ctrl+C fired!" ## # do clean up stuff ## quit() ## ## setControlCHook(ctrlc) + ## ``` when not defined(noSignalHandler) and not defined(useNimRtl): proc unsetControlCHook*() @@ -2509,279 +2292,20 @@ proc quit*(errormsg: string, errorcode = QuitFailure) {.noreturn.} = {.pop.} # checks: off # {.pop.} # hints: off -proc `/`*(x, y: int): float {.inline, noSideEffect.} = - ## Division of integers that results in a float. - ## - ## See also: - ## * `div <#div,int,int>`_ - ## * `mod <#mod,int,int>`_ - ## - ## .. code-block:: Nim - ## echo 7 / 5 # => 1.4 - result = toFloat(x) / toFloat(y) - -type - BackwardsIndex* = distinct int ## Type that is constructed by `^` for - ## reversed array accesses. - ## (See `^ template <#^.t,int>`_) - -template `^`*(x: int): BackwardsIndex = BackwardsIndex(x) - ## Builtin `roof`:idx: operator that can be used for convenient array access. - ## `a[^x]` is a shortcut for `a[a.len-x]`. - ## - ## .. code-block:: Nim - ## let - ## a = [1, 3, 5, 7, 9] - ## b = "abcdefgh" - ## - ## echo a[^1] # => 9 - ## echo b[^2] # => g - -template `..^`*(a, b: untyped): untyped = - ## A shortcut for `.. ^` to avoid the common gotcha that a space between - ## '..' and '^' is required. - a .. ^b - -template `..<`*(a, b: untyped): untyped = - ## A shortcut for `a .. pred(b)`. - ## - ## .. code-block:: Nim - ## for i in 5 ..< 9: - ## echo i # => 5; 6; 7; 8 - a .. (when b is BackwardsIndex: succ(b) else: pred(b)) - -template spliceImpl(s, a, L, b: untyped): untyped = - # make room for additional elements or cut: - var shift = b.len - max(0,L) # ignore negative slice size - var newLen = s.len + shift - if shift > 0: - # enlarge: - setLen(s, newLen) - for i in countdown(newLen-1, a+b.len): movingCopy(s[i], s[i-shift]) - else: - for i in countup(a+b.len, newLen-1): movingCopy(s[i], s[i-shift]) - # cut down: - setLen(s, newLen) - # fill the hole: - for i in 0 ..< b.len: s[a+i] = b[i] - -template `^^`(s, i: untyped): untyped = - (when i is BackwardsIndex: s.len - int(i) else: int(i)) - -template `[]`*(s: string; i: int): char = arrGet(s, i) -template `[]=`*(s: string; i: int; val: char) = arrPut(s, i, val) - -proc `[]`*[T, U: Ordinal](s: string, x: HSlice[T, U]): string {.inline.} = - ## Slice operation for strings. - ## Returns the inclusive range `[s[x.a], s[x.b]]`: - ## - ## .. code-block:: Nim - ## var s = "abcdef" - ## assert s[1..3] == "bcd" - let a = s ^^ x.a - let L = (s ^^ x.b) - a + 1 - result = newString(L) - for i in 0 ..< L: result[i] = s[i + a] - -proc `[]=`*[T, U: Ordinal](s: var string, x: HSlice[T, U], b: string) = - ## Slice assignment for strings. - ## - ## If `b.len` is not exactly the number of elements that are referred to - ## by `x`, a `splice`:idx: is performed: - ## - runnableExamples: - var s = "abcdefgh" - s[1 .. ^2] = "xyz" - assert s == "axyzh" - - var a = s ^^ x.a - var L = (s ^^ x.b) - a + 1 - if L == b.len: - for i in 0..`_. - -proc staticRead*(filename: string): string {.magic: "Slurp".} - ## Compile-time `readFile `_ proc for easy - ## `resource`:idx: embedding: - ## - ## The maximum file size limit that `staticRead` and `slurp` can read is - ## near or equal to the *free* memory of the device you are using to compile. - ## - ## .. code-block:: Nim - ## const myResource = staticRead"mydatafile.bin" - ## - ## `slurp <#slurp,string>`_ is an alias for `staticRead`. - -proc gorge*(command: string, input = "", cache = ""): string {. - magic: "StaticExec".} = discard - ## This is an alias for `staticExec <#staticExec,string,string,string>`_. - -proc staticExec*(command: string, input = "", cache = ""): string {. - magic: "StaticExec".} = discard - ## Executes an external process at compile-time and returns its text output - ## (stdout + stderr). - ## - ## If `input` is not an empty string, it will be passed as a standard input - ## to the executed program. - ## - ## .. code-block:: Nim - ## const buildInfo = "Revision " & staticExec("git rev-parse HEAD") & - ## "\nCompiled on " & staticExec("uname -v") - ## - ## `gorge <#gorge,string,string,string>`_ is an alias for `staticExec`. - ## - ## Note that you can use this proc inside a pragma like - ## `passc `_ or - ## `passl `_. - ## - ## If `cache` is not empty, the results of `staticExec` are cached within - ## the `nimcache` directory. Use `--forceBuild` to get rid of this caching - ## behaviour then. `command & input & cache` (the concatenated string) is - ## used to determine whether the entry in the cache is still valid. You can - ## use versioning information for `cache`: - ## - ## .. code-block:: Nim - ## const stateMachine = staticExec("dfaoptimizer", "input", "0.8.0") - -proc gorgeEx*(command: string, input = "", cache = ""): tuple[output: string, - exitCode: int] = - ## Similar to `gorge <#gorge,string,string,string>`_ but also returns the - ## precious exit code. - discard - - -proc `+=`*[T: float|float32|float64] (x: var T, y: T) {. - inline, noSideEffect.} = - ## Increments in place a floating point number. - x = x + y - -proc `-=`*[T: float|float32|float64] (x: var T, y: T) {. - inline, noSideEffect.} = - ## Decrements in place a floating point number. - x = x - y - -proc `*=`*[T: float|float32|float64] (x: var T, y: T) {. - inline, noSideEffect.} = - ## Multiplies in place a floating point number. - x = x * y - -proc `/=`*(x: var float64, y: float64) {.inline, noSideEffect.} = - ## Divides in place a floating point number. - x = x / y - -proc `/=`*[T: float|float32](x: var T, y: T) {.inline, noSideEffect.} = - ## Divides in place a floating point number. - x = x / y +include "system/indices" proc `&=`*(x: var string, y: string) {.magic: "AppendStrStr", noSideEffect.} ## Appends in place to a string. - ## - ## .. code-block:: Nim + ## ``` ## var a = "abc" ## a &= "de" # a <- "abcde" + ## ``` template `&=`*(x, y: typed) = ## Generic 'sink' operator for Nim. ## - ## For files an alias for `write`. ## If not specialized further, an alias for `add`. add(x, y) -when declared(File): - template `&=`*(f: File, x: typed) = write(f, x) - -template currentSourcePath*: string = instantiationInfo(-1, true).filename - ## Returns the full file-system path of the current source. - ## - ## To get the directory containing the current source, use it with - ## `os.parentDir() `_ as `currentSourcePath.parentDir()`. - ## - ## The path returned by this template is set at compile time. - ## - ## See the docstring of `macros.getProjectPath() `_ - ## for an example to see the distinction between the `currentSourcePath` - ## and `getProjectPath`. - ## - ## See also: - ## * `getCurrentDir proc `_ when compileOption("rangechecks"): template rangeCheck*(cond) = @@ -2821,36 +2345,35 @@ type NimNode* {.magic: "PNimrodNode".} = ref NimNodeObj ## Represents a Nim AST node. Macros operate on this type. -when defined(nimV2): - import system/repr_v2 - export repr_v2 +type + ForLoopStmt* {.compilerproc.} = object ## \ + ## A special type that marks a macro as a `for-loop macro`:idx:. + ## See `"For Loop Macro" `_. macro varargsLen*(x: varargs[untyped]): int {.since: (1, 1).} = ## returns number of variadic arguments in `x` proc varargsLenImpl(x: NimNode): NimNode {.magic: "LengthOpenArray", noSideEffect.} varargsLenImpl(x) -when false: - template eval*(blk: typed): typed = - ## Executes a block of code at compile time just as if it was a macro. - ## - ## Optionally, the block can return an AST tree that will replace the - ## eval expression. - macro payload: typed {.gensym.} = blk - payload() +when defined(nimV2): + import system/repr_v2 + export repr_v2 when hasAlloc or defined(nimscript): proc insert*(x: var string, item: string, i = 0.Natural) {.noSideEffect.} = ## Inserts `item` into `x` at position `i`. - ## - ## .. code-block:: Nim + ## ``` ## var a = "abc" ## a.insert("zz", 0) # a <- "zzabc" + ## ``` var xl = x.len setLen(x, xl+item.len) var j = xl-1 while j >= i: - shallowCopy(x[j+item.len], x[j]) + when defined(gcArc) or defined(gcOrc): + x[j+item.len] = move x[j] + else: + shallowCopy(x[j+item.len], x[j]) dec(j) j = 0 while j < item.len: @@ -2918,7 +2441,7 @@ proc addQuoted*[T](s: var string, x: T) = ## Users may overload `addQuoted` for custom (string-like) types if ## they want to implement a customized element representation. ## - ## .. code-block:: Nim + ## ``` ## var tmp = "" ## tmp.addQuoted(1) ## tmp.add(", ") @@ -2926,6 +2449,7 @@ proc addQuoted*[T](s: var string, x: T) = ## tmp.add(", ") ## tmp.addQuoted('c') ## assert(tmp == """1, "string", 'c'""") + ## ``` when T is string or T is cstring: s.add("\"") for c in x: @@ -2959,7 +2483,7 @@ proc locals*(): RootObj {.magic: "Plugin", noSideEffect.} = ## the official signature says, the return type is *not* `RootObj` but a ## tuple of a structure that depends on the current scope. Example: ## - ## .. code-block:: Nim + ## ``` ## proc testLocals() = ## var ## a = "something" @@ -2974,6 +2498,7 @@ proc locals*(): RootObj {.magic: "Plugin", noSideEffect.} = ## # -> name a with value something ## # -> name b with value 4 ## # -> B is 1 + ## ``` discard when hasAlloc and notJSnotNims: @@ -2997,10 +2522,10 @@ when hasAlloc and notJSnotNims: proc procCall*(x: untyped) {.magic: "ProcCall", compileTime.} = ## Special magic to prohibit dynamic binding for `method`:idx: calls. ## This is similar to `super`:idx: in ordinary OO languages. - ## - ## .. code-block:: Nim + ## ``` ## # 'someMethod' will be resolved fully statically: ## procCall someMethod(a, b) + ## ``` discard @@ -3013,17 +2538,6 @@ proc `==`*(x, y: cstring): bool {.magic: "EqCString", noSideEffect, elif x.isNil or y.isNil: result = false else: result = strcmp(x, y) == 0 -when true: # xxx PRTEMP remove - # bug #9149; ensure that 'typeof(nil)' does not match *too* well by using 'typeof(nil) | typeof(nil)', - # especially for converters, see tests/overload/tconverter_to_string.nim - # Eventually we will be able to remove this hack completely. - proc `==`*(x: string; y: typeof(nil) | typeof(nil)): bool {. - error: "'nil' is now invalid for 'string'".} = - discard - proc `==`*(x: typeof(nil) | typeof(nil); y: string): bool {. - error: "'nil' is now invalid for 'string'".} = - discard - template closureScope*(body: untyped): untyped = ## Useful when creating a closure in a loop to capture local loop variables by ## their current iteration values. @@ -3033,7 +2547,7 @@ template closureScope*(body: untyped): untyped = ## ## Example: ## - ## .. code-block:: Nim + ## ``` ## var myClosure : proc() ## # without closureScope: ## for i in 0 .. 5: @@ -3048,20 +2562,19 @@ template closureScope*(body: untyped): untyped = ## if j == 3: ## myClosure = proc() = echo j ## myClosure() # outputs 3 + ## ``` (proc() = body)() template once*(body: untyped): untyped = ## Executes a block of code only once (the first time the block is reached). - ## - ## .. code-block:: Nim - ## - ## proc draw(t: Triangle) = - ## once: - ## graphicsInit() - ## line(t.p1, t.p2) - ## line(t.p2, t.p3) - ## line(t.p3, t.p1) - ## + ## ``` + ## proc draw(t: Triangle) = + ## once: + ## graphicsInit() + ## line(t.p1, t.p2) + ## line(t.p2, t.p3) + ## line(t.p3, t.p1) + ## ``` var alreadyExecuted {.global.} = false if not alreadyExecuted: alreadyExecuted = true @@ -3121,11 +2634,6 @@ proc toOpenArrayByte*(x: openArray[char]; first, last: int): openArray[byte] {. proc toOpenArrayByte*(x: seq[char]; first, last: int): openArray[byte] {. magic: "Slice".} -type - ForLoopStmt* {.compilerproc.} = object ## \ - ## A special type that marks a macro as a `for-loop macro`:idx:. - ## See `"For Loop Macro" `_. - when defined(genode): var componentConstructHook*: proc (env: GenodeEnv) {.nimcall.} ## Hook into the Genode component bootstrap process. @@ -3150,9 +2658,79 @@ when defined(genode): import system/widestrs export widestrs +when notJSnotNims: + when defined(windows) and compileOption("threads"): + when not declared(addSysExitProc): + proc addSysExitProc(quitProc: proc() {.noconv.}) {.importc: "atexit", header: "".} + var echoLock: SysLock + initSysLock echoLock + addSysExitProc(proc() {.noconv.} = deinitSys(echoLock)) + + const stdOutLock = not defined(windows) and + not defined(android) and + not defined(nintendoswitch) and + not defined(freertos) and + not defined(zephyr) and + hostOS != "any" + + proc raiseEIO(msg: string) {.noinline, noreturn.} = + sysFatal(IOError, msg) + + proc echoBinSafe(args: openArray[string]) {.compilerproc.} = + when defined(androidNDK): + # When running nim in android app, stdout goes nowhere, so echo gets ignored + # To redirect echo to the android logcat, use -d:androidNDK + const ANDROID_LOG_VERBOSE = 2.cint + proc android_log_print(prio: cint, tag: cstring, fmt: cstring): cint + {.importc: "__android_log_print", header: "", varargs, discardable.} + var s = "" + for arg in args: + s.add arg + android_log_print(ANDROID_LOG_VERBOSE, "nim", s) + else: + # flockfile deadlocks some versions of Android 5.x.x + when stdOutLock: + proc flockfile(f: CFilePtr) {.importc, nodecl.} + proc funlockfile(f: CFilePtr) {.importc, nodecl.} + flockfile(cstdout) + when defined(windows) and compileOption("threads"): + acquireSys echoLock + for s in args: + when defined(windows): + # equivalent to syncio.writeWindows + proc writeWindows(f: CFilePtr; s: string; doRaise = false) = + # Don't ask why but the 'printf' family of function is the only thing + # that writes utf-8 strings reliably on Windows. At least on my Win 10 + # machine. We also enable `setConsoleOutputCP(65001)` now by default. + # But we cannot call printf directly as the string might contain \0. + # So we have to loop over all the sections separated by potential \0s. + var i = c_fprintf(f, "%s", s) + while i < s.len: + if s[i] == '\0': + let w = c_fputc('\0', f) + if w != 0: + if doRaise: raiseEIO("cannot write string to file") + break + inc i + else: + let w = c_fprintf(f, "%s", unsafeAddr s[i]) + if w <= 0: + if doRaise: raiseEIO("cannot write string to file") + break + inc i, w + writeWindows(cstdout, s) + else: + discard c_fwrite(s.cstring, cast[csize_t](s.len), 1, cstdout) + const linefeed = "\n" + discard c_fwrite(linefeed.cstring, linefeed.len, 1, cstdout) + discard c_fflush(cstdout) + when stdOutLock: + funlockfile(cstdout) + when defined(windows) and compileOption("threads"): + releaseSys echoLock + when not defined(nimPreviewSlimSystem): - {.deprecated: """io is about to move out of system; use `-d:nimPreviewSlimSystem` and - import `std/syncio`.""".} + {.deprecated: "io is about to move out of system; use `-d:nimPreviewSlimSystem` and import `std/syncio`".} import std/syncio export syncio diff --git a/lib/system/ansi_c.nim b/lib/system/ansi_c.nim index 23fb9fdef0d7..ae4b905048f7 100644 --- a/lib/system/ansi_c.nim +++ b/lib/system/ansi_c.nim @@ -116,21 +116,32 @@ elif defined(nimBuiltinSetjmp): proc c_builtin_setjmp(jmpb: ptr pointer): cint {. importc: "__builtin_setjmp", nodecl.} c_builtin_setjmp(unsafeAddr jmpb[0]) + elif defined(nimRawSetjmp) and not defined(nimStdSetjmp): when defined(windows): # No `_longjmp()` on Windows. proc c_longjmp*(jmpb: C_JmpBuf, retval: cint) {. header: "", importc: "longjmp".} - # The Windows `_setjmp()` takes two arguments, with the second being an - # undocumented buffer used by the SEH mechanism for stack unwinding. - # Mingw-w64 has been trying to get it right for years, but it's still - # prone to stack corruption during unwinding, so we disable that by setting - # it to NULL. - # More details: https://github.com/status-im/nimbus-eth2/issues/3121 - proc c_setjmp*(jmpb: C_JmpBuf): cint = - proc c_setjmp_win(jmpb: C_JmpBuf, ctx: pointer): cint {. - header: "", importc: "_setjmp".} - c_setjmp_win(jmpb, nil) + when defined(vcc) or defined(clangcl): + proc c_setjmp*(jmpb: C_JmpBuf): cint {. + header: "", importc: "setjmp".} + else: + # The Windows `_setjmp()` takes two arguments, with the second being an + # undocumented buffer used by the SEH mechanism for stack unwinding. + # Mingw-w64 has been trying to get it right for years, but it's still + # prone to stack corruption during unwinding, so we disable that by setting + # it to NULL. + # More details: https://github.com/status-im/nimbus-eth2/issues/3121 + when defined(nimHasStyleChecks): + {.push styleChecks: off.} + + proc c_setjmp*(jmpb: C_JmpBuf): cint = + proc c_setjmp_win(jmpb: C_JmpBuf, ctx: pointer): cint {. + header: "", importc: "_setjmp".} + c_setjmp_win(jmpb, nil) + + when defined(nimHasStyleChecks): + {.pop.} else: proc c_longjmp*(jmpb: C_JmpBuf, retval: cint) {. header: "", importc: "_longjmp".} @@ -169,6 +180,8 @@ proc c_printf*(frmt: cstring): cint {. proc c_fputs*(c: cstring, f: CFilePtr): cint {. importc: "fputs", header: "", discardable.} +proc c_fputc*(c: char, f: CFilePtr): cint {. + importc: "fputc", header: "", discardable.} proc c_sprintf*(buf, frmt: cstring): cint {. importc: "sprintf", header: "", varargs, noSideEffect.} @@ -201,7 +214,7 @@ else: proc c_fwrite*(buf: pointer, size, n: csize_t, f: CFilePtr): cint {. importc: "fwrite", header: "".} -proc c_fflush(f: CFilePtr): cint {. +proc c_fflush*(f: CFilePtr): cint {. importc: "fflush", header: "".} proc rawWriteString*(f: CFilePtr, s: cstring, length: int) {.compilerproc, nonReloadable, inline.} = diff --git a/lib/system/arc.nim b/lib/system/arc.nim index d66f4b997dc7..ccf9d44e2755 100644 --- a/lib/system/arc.nim +++ b/lib/system/arc.nim @@ -28,8 +28,8 @@ ObjectA's ``name`` is "|ObjectA|RootObj|". ObjectB's ``name`` is "|ObjectB|ObjectA|RootObj|". Now to check for ``x of ObjectB`` we need to check -for ``x.typ.name.hasSubstring("|ObjectB|")``. In the actual implementation, -however, we could also use a +for ``x.typ.name.endsWith("|ObjectB|ObjectA|RootObj|")``. +In the actual implementation, however, we could also use a hash of ``package & "." & module & "." & name`` to save space. ]# @@ -227,10 +227,34 @@ template tearDownForeignThreadGc* = ## With `--gc:arc` a nop. discard +type ObjCheckCache = array[0..1, PNimTypeV2] + +proc memcmp(str1, str2: cstring, n: csize_t): cint {.importc, header: "".} + +func endsWith(s, suffix: cstring): bool {.inline.} = + let + sLen = s.len + suffixLen = suffix.len + + if suffixLen <= sLen: + result = memcmp(cstring(addr s[sLen - suffixLen]), suffix, csize_t(suffixLen)) == 0 + proc isObj(obj: PNimTypeV2, subclass: cstring): bool {.compilerRtl, inl.} = - proc strstr(s, sub: cstring): cstring {.header: "", importc.} + result = endsWith(obj.name, subclass) - result = strstr(obj.name, subclass) != nil +proc isObjSlowPath(obj: PNimTypeV2, subclass: cstring, cache: var ObjCheckCache): bool {.compilerRtl, inline.} = + if endsWith(obj.name, subclass): + cache[1] = obj + result = true + else: + cache[0] = obj + result = false + +proc isObjWithCache(obj: PNimTypeV2, subclass: cstring, cache: var ObjCheckCache): bool {.compilerRtl.} = + if cache[0] == obj: result = false + elif cache[1] == obj: result = true + else: + result = isObjSlowPath(obj, subclass, cache) proc chckObj(obj: PNimTypeV2, subclass: cstring) {.compilerRtl.} = # checks if obj is of type subclass: diff --git a/lib/system/arithmetics.nim b/lib/system/arithmetics.nim index 0dd329495fe5..910e735073f3 100644 --- a/lib/system/arithmetics.nim +++ b/lib/system/arithmetics.nim @@ -45,109 +45,6 @@ proc dec*[T: Ordinal](x: var T, y = 1) {.magic: "Dec", noSideEffect.} = # -------------------------------------------------------------------------- # built-in operators -when defined(nimNoZeroExtendMagic): - proc ze*(x: int8): int {.deprecated.} = - ## zero extends a smaller integer type to `int`. This treats `x` as - ## unsigned. - ## **Deprecated since version 0.19.9**: Use unsigned integers instead. - cast[int](uint(cast[uint8](x))) - - proc ze*(x: int16): int {.deprecated.} = - ## zero extends a smaller integer type to `int`. This treats `x` as - ## unsigned. - ## **Deprecated since version 0.19.9**: Use unsigned integers instead. - cast[int](uint(cast[uint16](x))) - - proc ze64*(x: int8): int64 {.deprecated.} = - ## zero extends a smaller integer type to `int64`. This treats `x` as - ## unsigned. - ## **Deprecated since version 0.19.9**: Use unsigned integers instead. - cast[int64](uint64(cast[uint8](x))) - - proc ze64*(x: int16): int64 {.deprecated.} = - ## zero extends a smaller integer type to `int64`. This treats `x` as - ## unsigned. - ## **Deprecated since version 0.19.9**: Use unsigned integers instead. - cast[int64](uint64(cast[uint16](x))) - - proc ze64*(x: int32): int64 {.deprecated.} = - ## zero extends a smaller integer type to `int64`. This treats `x` as - ## unsigned. - ## **Deprecated since version 0.19.9**: Use unsigned integers instead. - cast[int64](uint64(cast[uint32](x))) - - proc ze64*(x: int): int64 {.deprecated.} = - ## zero extends a smaller integer type to `int64`. This treats `x` as - ## unsigned. Does nothing if the size of an `int` is the same as `int64`. - ## (This is the case on 64 bit processors.) - ## **Deprecated since version 0.19.9**: Use unsigned integers instead. - cast[int64](uint64(cast[uint](x))) - - proc toU8*(x: int): int8 {.deprecated.} = - ## treats `x` as unsigned and converts it to a byte by taking the last 8 bits - ## from `x`. - ## **Deprecated since version 0.19.9**: Use unsigned integers instead. - cast[int8](x) - - proc toU16*(x: int): int16 {.deprecated.} = - ## treats `x` as unsigned and converts it to an `int16` by taking the last - ## 16 bits from `x`. - ## **Deprecated since version 0.19.9**: Use unsigned integers instead. - cast[int16](x) - - proc toU32*(x: int64): int32 {.deprecated.} = - ## treats `x` as unsigned and converts it to an `int32` by taking the - ## last 32 bits from `x`. - ## **Deprecated since version 0.19.9**: Use unsigned integers instead. - cast[int32](x) - -elif not defined(js): - proc ze*(x: int8): int {.magic: "Ze8ToI", noSideEffect, deprecated.} - ## zero extends a smaller integer type to `int`. This treats `x` as - ## unsigned. - ## **Deprecated since version 0.19.9**: Use unsigned integers instead. - - proc ze*(x: int16): int {.magic: "Ze16ToI", noSideEffect, deprecated.} - ## zero extends a smaller integer type to `int`. This treats `x` as - ## unsigned. - ## **Deprecated since version 0.19.9**: Use unsigned integers instead. - - proc ze64*(x: int8): int64 {.magic: "Ze8ToI64", noSideEffect, deprecated.} - ## zero extends a smaller integer type to `int64`. This treats `x` as - ## unsigned. - ## **Deprecated since version 0.19.9**: Use unsigned integers instead. - - proc ze64*(x: int16): int64 {.magic: "Ze16ToI64", noSideEffect, deprecated.} - ## zero extends a smaller integer type to `int64`. This treats `x` as - ## unsigned. - ## **Deprecated since version 0.19.9**: Use unsigned integers instead. - - proc ze64*(x: int32): int64 {.magic: "Ze32ToI64", noSideEffect, deprecated.} - ## zero extends a smaller integer type to `int64`. This treats `x` as - ## unsigned. - ## **Deprecated since version 0.19.9**: Use unsigned integers instead. - - proc ze64*(x: int): int64 {.magic: "ZeIToI64", noSideEffect, deprecated.} - ## zero extends a smaller integer type to `int64`. This treats `x` as - ## unsigned. Does nothing if the size of an `int` is the same as `int64`. - ## (This is the case on 64 bit processors.) - ## **Deprecated since version 0.19.9**: Use unsigned integers instead. - - proc toU8*(x: int): int8 {.magic: "ToU8", noSideEffect, deprecated.} - ## treats `x` as unsigned and converts it to a byte by taking the last 8 bits - ## from `x`. - ## **Deprecated since version 0.19.9**: Use unsigned integers instead. - - proc toU16*(x: int): int16 {.magic: "ToU16", noSideEffect, deprecated.} - ## treats `x` as unsigned and converts it to an `int16` by taking the last - ## 16 bits from `x`. - ## **Deprecated since version 0.19.9**: Use unsigned integers instead. - - proc toU32*(x: int64): int32 {.magic: "ToU32", noSideEffect, deprecated.} - ## treats `x` as unsigned and converts it to an `int32` by taking the - ## last 32 bits from `x`. - ## **Deprecated since version 0.19.9**: Use unsigned integers instead. - # integer calculations: proc `+`*(x: int): int {.magic: "UnaryPlusI", noSideEffect.} ## Unary `+` operator for an integer. Has no effect. @@ -399,6 +296,59 @@ proc `mod`*(x, y: uint16): uint16 {.magic: "ModU", noSideEffect.} proc `mod`*(x, y: uint32): uint32 {.magic: "ModU", noSideEffect.} proc `mod`*(x, y: uint64): uint64 {.magic: "ModU", noSideEffect.} +proc `+=`*[T: SomeInteger](x: var T, y: T) {. + magic: "Inc", noSideEffect.} + ## Increments an integer. + +proc `-=`*[T: SomeInteger](x: var T, y: T) {. + magic: "Dec", noSideEffect.} + ## Decrements an integer. + +proc `*=`*[T: SomeInteger](x: var T, y: T) {. + inline, noSideEffect.} = + ## Binary `*=` operator for integers. + x = x * y + +# floating point operations: +proc `+`*(x: float32): float32 {.magic: "UnaryPlusF64", noSideEffect.} +proc `-`*(x: float32): float32 {.magic: "UnaryMinusF64", noSideEffect.} +proc `+`*(x, y: float32): float32 {.magic: "AddF64", noSideEffect.} +proc `-`*(x, y: float32): float32 {.magic: "SubF64", noSideEffect.} +proc `*`*(x, y: float32): float32 {.magic: "MulF64", noSideEffect.} +proc `/`*(x, y: float32): float32 {.magic: "DivF64", noSideEffect.} + +proc `+`*(x: float): float {.magic: "UnaryPlusF64", noSideEffect.} +proc `-`*(x: float): float {.magic: "UnaryMinusF64", noSideEffect.} +proc `+`*(x, y: float): float {.magic: "AddF64", noSideEffect.} +proc `-`*(x, y: float): float {.magic: "SubF64", noSideEffect.} +proc `*`*(x, y: float): float {.magic: "MulF64", noSideEffect.} +proc `/`*(x, y: float): float {.magic: "DivF64", noSideEffect.} + +proc `+=`*[T: float|float32|float64] (x: var T, y: T) {. + inline, noSideEffect.} = + ## Increments in place a floating point number. + x = x + y + +proc `-=`*[T: float|float32|float64] (x: var T, y: T) {. + inline, noSideEffect.} = + ## Decrements in place a floating point number. + x = x - y + +proc `*=`*[T: float|float32|float64] (x: var T, y: T) {. + inline, noSideEffect.} = + ## Multiplies in place a floating point number. + x = x * y + +proc `/=`*(x: var float64, y: float64) {.inline, noSideEffect.} = + ## Divides in place a floating point number. + x = x / y + +proc `/=`*[T: float|float32](x: var T, y: T) {.inline, noSideEffect.} = + ## Divides in place a floating point number. + x = x / y + +# the following have to be included in system, not imported for some reason: + proc `+%`*(x, y: int): int {.inline.} = ## Treats `x` and `y` as unsigned and adds them. ## @@ -454,15 +404,106 @@ proc `%%`*(x, y: int16): int16 {.inline.} = cast[int16](cast[uint16](x) mod cast proc `%%`*(x, y: int32): int32 {.inline.} = cast[int32](cast[uint32](x) mod cast[uint32](y)) proc `%%`*(x, y: int64): int64 {.inline.} = cast[int64](cast[uint64](x) mod cast[uint64](y)) -proc `+=`*[T: SomeInteger](x: var T, y: T) {. - magic: "Inc", noSideEffect.} - ## Increments an integer. - -proc `-=`*[T: SomeInteger](x: var T, y: T) {. - magic: "Dec", noSideEffect.} - ## Decrements an integer. - -proc `*=`*[T: SomeInteger](x: var T, y: T) {. - inline, noSideEffect.} = - ## Binary `*=` operator for integers. - x = x * y +when not defined(nimPreviewSlimSystem): + when defined(nimNoZeroExtendMagic): + proc ze*(x: int8): int {.deprecated.} = + ## zero extends a smaller integer type to `int`. This treats `x` as + ## unsigned. + ## **Deprecated since version 0.19.9**: Use unsigned integers instead. + cast[int](uint(cast[uint8](x))) + + proc ze*(x: int16): int {.deprecated.} = + ## zero extends a smaller integer type to `int`. This treats `x` as + ## unsigned. + ## **Deprecated since version 0.19.9**: Use unsigned integers instead. + cast[int](uint(cast[uint16](x))) + + proc ze64*(x: int8): int64 {.deprecated.} = + ## zero extends a smaller integer type to `int64`. This treats `x` as + ## unsigned. + ## **Deprecated since version 0.19.9**: Use unsigned integers instead. + cast[int64](uint64(cast[uint8](x))) + + proc ze64*(x: int16): int64 {.deprecated.} = + ## zero extends a smaller integer type to `int64`. This treats `x` as + ## unsigned. + ## **Deprecated since version 0.19.9**: Use unsigned integers instead. + cast[int64](uint64(cast[uint16](x))) + + proc ze64*(x: int32): int64 {.deprecated.} = + ## zero extends a smaller integer type to `int64`. This treats `x` as + ## unsigned. + ## **Deprecated since version 0.19.9**: Use unsigned integers instead. + cast[int64](uint64(cast[uint32](x))) + + proc ze64*(x: int): int64 {.deprecated.} = + ## zero extends a smaller integer type to `int64`. This treats `x` as + ## unsigned. Does nothing if the size of an `int` is the same as `int64`. + ## (This is the case on 64 bit processors.) + ## **Deprecated since version 0.19.9**: Use unsigned integers instead. + cast[int64](uint64(cast[uint](x))) + + proc toU8*(x: int): int8 {.deprecated.} = + ## treats `x` as unsigned and converts it to a byte by taking the last 8 bits + ## from `x`. + ## **Deprecated since version 0.19.9**: Use unsigned integers instead. + cast[int8](x) + + proc toU16*(x: int): int16 {.deprecated.} = + ## treats `x` as unsigned and converts it to an `int16` by taking the last + ## 16 bits from `x`. + ## **Deprecated since version 0.19.9**: Use unsigned integers instead. + cast[int16](x) + + proc toU32*(x: int64): int32 {.deprecated.} = + ## treats `x` as unsigned and converts it to an `int32` by taking the + ## last 32 bits from `x`. + ## **Deprecated since version 0.19.9**: Use unsigned integers instead. + cast[int32](x) + + elif not defined(js): + proc ze*(x: int8): int {.magic: "Ze8ToI", noSideEffect, deprecated.} + ## zero extends a smaller integer type to `int`. This treats `x` as + ## unsigned. + ## **Deprecated since version 0.19.9**: Use unsigned integers instead. + + proc ze*(x: int16): int {.magic: "Ze16ToI", noSideEffect, deprecated.} + ## zero extends a smaller integer type to `int`. This treats `x` as + ## unsigned. + ## **Deprecated since version 0.19.9**: Use unsigned integers instead. + + proc ze64*(x: int8): int64 {.magic: "Ze8ToI64", noSideEffect, deprecated.} + ## zero extends a smaller integer type to `int64`. This treats `x` as + ## unsigned. + ## **Deprecated since version 0.19.9**: Use unsigned integers instead. + + proc ze64*(x: int16): int64 {.magic: "Ze16ToI64", noSideEffect, deprecated.} + ## zero extends a smaller integer type to `int64`. This treats `x` as + ## unsigned. + ## **Deprecated since version 0.19.9**: Use unsigned integers instead. + + proc ze64*(x: int32): int64 {.magic: "Ze32ToI64", noSideEffect, deprecated.} + ## zero extends a smaller integer type to `int64`. This treats `x` as + ## unsigned. + ## **Deprecated since version 0.19.9**: Use unsigned integers instead. + + proc ze64*(x: int): int64 {.magic: "ZeIToI64", noSideEffect, deprecated.} + ## zero extends a smaller integer type to `int64`. This treats `x` as + ## unsigned. Does nothing if the size of an `int` is the same as `int64`. + ## (This is the case on 64 bit processors.) + ## **Deprecated since version 0.19.9**: Use unsigned integers instead. + + proc toU8*(x: int): int8 {.magic: "ToU8", noSideEffect, deprecated.} + ## treats `x` as unsigned and converts it to a byte by taking the last 8 bits + ## from `x`. + ## **Deprecated since version 0.19.9**: Use unsigned integers instead. + + proc toU16*(x: int): int16 {.magic: "ToU16", noSideEffect, deprecated.} + ## treats `x` as unsigned and converts it to an `int16` by taking the last + ## 16 bits from `x`. + ## **Deprecated since version 0.19.9**: Use unsigned integers instead. + + proc toU32*(x: int64): int32 {.magic: "ToU32", noSideEffect, deprecated.} + ## treats `x` as unsigned and converts it to an `int32` by taking the + ## last 32 bits from `x`. + ## **Deprecated since version 0.19.9**: Use unsigned integers instead. diff --git a/lib/system/basic_types.nim b/lib/system/basic_types.nim index 7779e1ce90aa..bf81b9b6a34c 100644 --- a/lib/system/basic_types.nim +++ b/lib/system/basic_types.nim @@ -1,15 +1,45 @@ type - int* {.magic: "Int".} ## Default integer type; bitwidth depends on + int* {.magic: Int.} ## Default integer type; bitwidth depends on ## architecture, but is always the same as a pointer. - int8* {.magic: "Int8".} ## Signed 8 bit integer type. - int16* {.magic: "Int16".} ## Signed 16 bit integer type. - int32* {.magic: "Int32".} ## Signed 32 bit integer type. - int64* {.magic: "Int64".} ## Signed 64 bit integer type. - uint* {.magic: "UInt".} ## Unsigned default integer type. - uint8* {.magic: "UInt8".} ## Unsigned 8 bit integer type. - uint16* {.magic: "UInt16".} ## Unsigned 16 bit integer type. - uint32* {.magic: "UInt32".} ## Unsigned 32 bit integer type. - uint64* {.magic: "UInt64".} ## Unsigned 64 bit integer type. + int8* {.magic: Int8.} ## Signed 8 bit integer type. + int16* {.magic: Int16.} ## Signed 16 bit integer type. + int32* {.magic: Int32.} ## Signed 32 bit integer type. + int64* {.magic: Int64.} ## Signed 64 bit integer type. + uint* {.magic: UInt.} ## Unsigned default integer type. + uint8* {.magic: UInt8.} ## Unsigned 8 bit integer type. + uint16* {.magic: UInt16.} ## Unsigned 16 bit integer type. + uint32* {.magic: UInt32.} ## Unsigned 32 bit integer type. + uint64* {.magic: UInt64.} ## Unsigned 64 bit integer type. + +type + float* {.magic: Float.} ## Default floating point type. + float32* {.magic: Float32.} ## 32 bit floating point type. + float64* {.magic: Float.} ## 64 bit floating point type. + +# 'float64' is now an alias to 'float'; this solves many problems + +type + char* {.magic: Char.} ## Built-in 8 bit character type (unsigned). + string* {.magic: String.} ## Built-in string type. + cstring* {.magic: Cstring.} ## Built-in cstring (*compatible string*) type. + pointer* {.magic: Pointer.} ## Built-in pointer type, use the `addr` + ## operator to get a pointer to a variable. + + typedesc* {.magic: TypeDesc.} ## Meta type to denote a type description. + +type + `ptr`*[T] {.magic: Pointer.} ## Built-in generic untraced pointer type. + `ref`*[T] {.magic: Pointer.} ## Built-in generic traced pointer type. + + `nil` {.magic: "Nil".} + + void* {.magic: "VoidType".} ## Meta type to denote the absence of any type. + auto* {.magic: Expr.} ## Meta type for automatic type determination. + any* {.deprecated: "Deprecated since v1.5; Use auto instead.".} = distinct auto ## Deprecated; Use `auto` instead. See https://github.com/nim-lang/RFCs/issues/281 + untyped* {.magic: Expr.} ## Meta type to denote an expression that + ## is not resolved (for templates). + typed* {.magic: Stmt.} ## Meta type to denote an expression that + ## is resolved (for templates). type # we need to start a new type section here, so that ``0`` can have a type bool* {.magic: "Bool".} = enum ## Built-in boolean type. @@ -29,15 +59,16 @@ type SomeInteger* = SomeSignedInt|SomeUnsignedInt ## Type class matching all integer types. + SomeFloat* = float|float32|float64 + ## Type class matching all floating point number types. + + SomeNumber* = SomeInteger|SomeFloat + ## Type class matching all number types. + SomeOrdinal* = int|int8|int16|int32|int64|bool|enum|uint|uint8|uint16|uint32|uint64 ## Type class matching all ordinal types; however this includes enums with ## holes. See also `Ordinal` - BiggestInt* = int64 - ## is an alias for the biggest signed integer type the Nim compiler - ## supports. Currently this is `int64`, but it is platform-dependent - ## in general. - {.push warning[GcMem]: off, warning[Uninit]: off.} {.push hints: off.} diff --git a/lib/system/chcks.nim b/lib/system/chcks.nim index a404e9d40b33..dd26d140d4ac 100644 --- a/lib/system/chcks.nim +++ b/lib/system/chcks.nim @@ -9,6 +9,8 @@ # Implementation of some runtime checks. include system/indexerrors +when defined(nimPreviewSlimSystem): + import std/formatfloat proc raiseRangeError(val: BiggestInt) {.compilerproc, noinline.} = when hostOS == "standalone": @@ -16,6 +18,9 @@ proc raiseRangeError(val: BiggestInt) {.compilerproc, noinline.} = else: sysFatal(RangeDefect, "value out of range: ", $val) +proc raiseIndexError4(l1, h1, h2: int) {.compilerproc, noinline.} = + sysFatal(IndexDefect, "index out of bounds: " & $l1 & ".." & $h1 & " notin 0.." & $(h2 - 1)) + proc raiseIndexError3(i, a, b: int) {.compilerproc, noinline.} = sysFatal(IndexDefect, formatErrorIndexBound(i, a, b)) @@ -32,11 +37,11 @@ proc raiseFieldError(f: string) {.compilerproc, noinline.} = when defined(nimV2): proc raiseFieldError2(f: string, discVal: int) {.compilerproc, noinline.} = ## raised when field is inaccessible given runtime value of discriminant - sysFatal(FieldError, f & $discVal & "'") + sysFatal(FieldDefect, f & $discVal & "'") else: proc raiseFieldError2(f: string, discVal: string) {.compilerproc, noinline.} = ## raised when field is inaccessible given runtime value of discriminant - sysFatal(FieldError, formatFieldDefect(f, discVal)) + sysFatal(FieldDefect, formatFieldDefect(f, discVal)) proc raiseRangeErrorI(i, a, b: BiggestInt) {.compilerproc, noinline.} = when defined(standalone): diff --git a/lib/system/comparisons.nim b/lib/system/comparisons.nim index 7122daa20952..eedac9d840a2 100644 --- a/lib/system/comparisons.nim +++ b/lib/system/comparisons.nim @@ -206,6 +206,14 @@ proc `==`*(x, y: uint16): bool {.magic: "EqI", noSideEffect.} proc `==`*(x, y: uint32): bool {.magic: "EqI", noSideEffect.} proc `==`*(x, y: uint64): bool {.magic: "EqI", noSideEffect.} +proc `<=`*(x, y: float32): bool {.magic: "LeF64", noSideEffect.} +proc `<=`*(x, y: float): bool {.magic: "LeF64", noSideEffect.} + +proc `<`*(x, y: float32): bool {.magic: "LtF64", noSideEffect.} +proc `<`*(x, y: float): bool {.magic: "LtF64", noSideEffect.} + +proc `==`*(x, y: float32): bool {.magic: "EqF64", noSideEffect.} +proc `==`*(x, y: float): bool {.magic: "EqF64", noSideEffect.} {.push stackTrace: off.} @@ -220,6 +228,13 @@ proc min*(x, y: int32): int32 {.magic: "MinI", noSideEffect.} = proc min*(x, y: int64): int64 {.magic: "MinI", noSideEffect.} = ## The minimum value of two integers. if x <= y: x else: y +proc min*(x, y: float32): float32 {.noSideEffect, inline.} = + if x <= y or y != y: x else: y +proc min*(x, y: float64): float64 {.noSideEffect, inline.} = + if x <= y or y != y: x else: y +proc min*[T: not SomeFloat](x, y: T): T {.inline.} = + ## Generic minimum operator of 2 values based on `<=`. + if x <= y: x else: y proc max*(x, y: int): int {.magic: "MaxI", noSideEffect.} = if y <= x: x else: y @@ -232,6 +247,13 @@ proc max*(x, y: int32): int32 {.magic: "MaxI", noSideEffect.} = proc max*(x, y: int64): int64 {.magic: "MaxI", noSideEffect.} = ## The maximum value of two integers. if y <= x: x else: y +proc max*(x, y: float32): float32 {.noSideEffect, inline.} = + if y <= x or y != y: x else: y +proc max*(x, y: float64): float64 {.noSideEffect, inline.} = + if y <= x or y != y: x else: y +proc max*[T: not SomeFloat](x, y: T): T {.inline.} = + ## Generic maximum operator of 2 values based on `<=`. + if y <= x: x else: y proc min*[T](x: openArray[T]): T = @@ -250,7 +272,7 @@ proc max*[T](x: openArray[T]): T = proc clamp*[T](x, a, b: T): T = - ## Limits the value `x` within the interval [a, b]. + ## Limits the value `x` within the interval \[a, b]. ## This proc is equivalent to but faster than `max(a, min(b, x))`. ## ## .. warning:: `a <= b` is assumed and will not be checked (currently). diff --git a/lib/system/compilation.nim b/lib/system/compilation.nim new file mode 100644 index 000000000000..6109e9874da8 --- /dev/null +++ b/lib/system/compilation.nim @@ -0,0 +1,214 @@ +const + NimMajor* {.intdefine.}: int = 1 + ## is the major number of Nim's version. Example: + ## ``` + ## when (NimMajor, NimMinor, NimPatch) >= (1, 3, 1): discard + ## ``` + # see also std/private/since + + NimMinor* {.intdefine.}: int = 7 + ## is the minor number of Nim's version. + ## Odd for devel, even for releases. + + NimPatch* {.intdefine.}: int = 3 + ## is the patch number of Nim's version. + ## Odd for devel, even for releases. + +{.push profiler: off.} +let nimvm* {.magic: "Nimvm", compileTime.}: bool = false + ## May be used only in `when` expression. + ## It is true in Nim VM context and false otherwise. +{.pop.} + +const + isMainModule* {.magic: "IsMainModule".}: bool = false + ## True only when accessed in the main module. This works thanks to + ## compiler magic. It is useful to embed testing code in a module. + + CompileDate* {.magic: "CompileDate".}: string = "0000-00-00" + ## 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" + ## The time (in UTC) of compilation as a string of the form + ## `HH:MM:SS`. This works thanks to compiler magic. + +proc defined*(x: untyped): bool {.magic: "Defined", noSideEffect, compileTime.} + ## Special compile-time procedure that checks whether `x` is + ## defined. + ## + ## `x` is an external symbol introduced through the compiler's + ## `-d:x switch `_ to enable + ## build time conditionals: + ## ``` + ## when not defined(release): + ## # Do here programmer friendly expensive sanity checks. + ## # Put here the normal code + ## ``` + ## + ## See also: + ## * `compileOption <#compileOption,string>`_ for `on|off` options + ## * `compileOption <#compileOption,string,string>`_ for enum options + ## * `define pragmas `_ + +when defined(nimHasDeclaredMagic): + proc declared*(x: untyped): bool {.magic: "Declared", noSideEffect, compileTime.} + ## Special compile-time procedure that checks whether `x` is + ## declared. `x` has to be an identifier or a qualified identifier. + ## + ## This can be used to check whether a library provides a certain + ## feature or not: + ## ``` + ## when not declared(strutils.toUpper): + ## # provide our own toUpper proc here, because strutils is + ## # missing it. + ## ``` + ## + ## See also: + ## * `declaredInScope <#declaredInScope,untyped>`_ +else: + proc declared*(x: untyped): bool {.magic: "Defined", noSideEffect, compileTime.} + +when defined(nimHasDeclaredMagic): + proc declaredInScope*(x: untyped): bool {.magic: "DeclaredInScope", noSideEffect, compileTime.} + ## Special compile-time procedure that checks whether `x` is + ## declared in the current scope. `x` has to be an identifier. +else: + proc declaredInScope*(x: untyped): bool {.magic: "DefinedInScope", noSideEffect, compileTime.} + +proc compiles*(x: untyped): bool {.magic: "Compiles", noSideEffect, compileTime.} = + ## Special compile-time procedure that checks whether `x` can be compiled + ## without any semantic error. + ## This can be used to check whether a type supports some operation: + ## ``` + ## when compiles(3 + 4): + ## echo "'+' for integers is available" + ## ``` + discard + +proc astToStr*[T](x: T): string {.magic: "AstToStr", noSideEffect.} + ## Converts the AST of `x` into a string representation. This is very useful + ## for debugging. + +proc runnableExamples*(rdoccmd = "", body: untyped) {.magic: "RunnableExamples".} = + ## A section you should use to mark `runnable example`:idx: code with. + ## + ## - In normal debug and release builds code within + ## a `runnableExamples` section is ignored. + ## - The documentation generator is aware of these examples and considers them + ## part of the `##` doc comment. As the last step of documentation + ## generation each runnableExample is put in its own file `$file_examples$i.nim`, + ## compiled and tested. The collected examples are + ## put into their own module to ensure the examples do not refer to + ## non-exported symbols. + runnableExamples: + proc timesTwo*(x: int): int = + ## This proc doubles a number. + runnableExamples: + # at module scope + const exported* = 123 + assert timesTwo(5) == 10 + block: # at block scope + defer: echo "done" + runnableExamples "-d:foo -b:cpp": + import std/compilesettings + assert querySetting(backend) == "cpp" + assert defined(foo) + runnableExamples "-r:off": ## this one is only compiled + import std/browsers + openDefaultBrowser "https://forum.nim-lang.org/" + 2 * x + +proc compileOption*(option: string): bool {. + magic: "CompileOption", noSideEffect.} = + ## Can be used to determine an `on|off` compile-time option. + ## + ## See also: + ## * `compileOption <#compileOption,string,string>`_ for enum options + ## * `defined <#defined,untyped>`_ + ## * `std/compilesettings module `_ + runnableExamples("--floatChecks:off"): + static: doAssert not compileOption("floatchecks") + {.push floatChecks: on.} + static: doAssert compileOption("floatchecks") + # floating point NaN and Inf checks enabled in this scope + {.pop.} + +proc compileOption*(option, arg: string): bool {. + magic: "CompileOptionArg", noSideEffect.} = + ## Can be used to determine an enum compile-time option. + ## + ## See also: + ## * `compileOption <#compileOption,string>`_ for `on|off` options + ## * `defined <#defined,untyped>`_ + ## * `std/compilesettings module `_ + runnableExamples: + when compileOption("opt", "size") and compileOption("gc", "boehm"): + discard "compiled with optimization for size and uses Boehm's GC" + +template currentSourcePath*: string = instantiationInfo(-1, true).filename + ## Returns the full file-system path of the current source. + ## + ## To get the directory containing the current source, use it with + ## `os.parentDir() `_ as `currentSourcePath.parentDir()`. + ## + ## The path returned by this template is set at compile time. + ## + ## See the docstring of `macros.getProjectPath() `_ + ## for an example to see the distinction between the `currentSourcePath` + ## and `getProjectPath`. + ## + ## See also: + ## * `getCurrentDir proc `_ + +proc slurp*(filename: string): string {.magic: "Slurp".} + ## This is an alias for `staticRead <#staticRead,string>`_. + +proc staticRead*(filename: string): string {.magic: "Slurp".} + ## Compile-time `readFile `_ proc for easy + ## `resource`:idx: embedding: + ## + ## The maximum file size limit that `staticRead` and `slurp` can read is + ## near or equal to the *free* memory of the device you are using to compile. + ## ``` + ## const myResource = staticRead"mydatafile.bin" + ## ``` + ## + ## `slurp <#slurp,string>`_ is an alias for `staticRead`. + +proc gorge*(command: string, input = "", cache = ""): string {. + magic: "StaticExec".} = discard + ## This is an alias for `staticExec <#staticExec,string,string,string>`_. + +proc staticExec*(command: string, input = "", cache = ""): string {. + magic: "StaticExec".} = discard + ## Executes an external process at compile-time and returns its text output + ## (stdout + stderr). + ## + ## If `input` is not an empty string, it will be passed as a standard input + ## to the executed program. + ## ``` + ## const buildInfo = "Revision " & staticExec("git rev-parse HEAD") & + ## "\nCompiled on " & staticExec("uname -v") + ## ``` + ## + ## `gorge <#gorge,string,string,string>`_ is an alias for `staticExec`. + ## + ## Note that you can use this proc inside a pragma like + ## `passc `_ or + ## `passl `_. + ## + ## If `cache` is not empty, the results of `staticExec` are cached within + ## the `nimcache` directory. Use `--forceBuild` to get rid of this caching + ## behaviour then. `command & input & cache` (the concatenated string) is + ## used to determine whether the entry in the cache is still valid. You can + ## use versioning information for `cache`: + ## ``` + ## const stateMachine = staticExec("dfaoptimizer", "input", "0.8.0") + ## ``` + +proc gorgeEx*(command: string, input = "", cache = ""): tuple[output: string, + exitCode: int] = + ## Similar to `gorge <#gorge,string,string,string>`_ but also returns the + ## precious exit code. + discard diff --git a/lib/system/ctypes.nim b/lib/system/ctypes.nim new file mode 100644 index 000000000000..6ba28ed496fc --- /dev/null +++ b/lib/system/ctypes.nim @@ -0,0 +1,90 @@ +## Some type definitions for compatibility between different +## backends and platforms. + +type + BiggestInt* = int64 + ## is an alias for the biggest signed integer type the Nim compiler + ## supports. Currently this is `int64`, but it is platform-dependent + ## in general. + + BiggestFloat* = float64 + ## is an alias for the biggest floating point type the Nim + ## compiler supports. Currently this is `float64`, but it is + ## platform-dependent in general. + +when defined(js): + type BiggestUInt* = uint32 + ## is an alias for the biggest unsigned integer type the Nim compiler + ## supports. Currently this is `uint32` for JS and `uint64` for other + ## targets. +else: + type BiggestUInt* = uint64 + ## is an alias for the biggest unsigned integer type the Nim compiler + ## supports. Currently this is `uint32` for JS and `uint64` for other + ## targets. + +when defined(windows): + type + clong* {.importc: "long", nodecl.} = int32 + ## This is the same as the type `long` in *C*. + culong* {.importc: "unsigned long", nodecl.} = uint32 + ## This is the same as the type `unsigned long` in *C*. +else: + type + clong* {.importc: "long", nodecl.} = int + ## This is the same as the type `long` in *C*. + culong* {.importc: "unsigned long", nodecl.} = uint + ## This is the same as the type `unsigned long` in *C*. + +type # these work for most platforms: + cchar* {.importc: "char", nodecl.} = char + ## This is the same as the type `char` in *C*. + cschar* {.importc: "signed char", nodecl.} = int8 + ## This is the same as the type `signed char` in *C*. + cshort* {.importc: "short", nodecl.} = int16 + ## This is the same as the type `short` in *C*. + cint* {.importc: "int", nodecl.} = int32 + ## This is the same as the type `int` in *C*. + csize_t* {.importc: "size_t", nodecl.} = uint + ## This is the same as the type `size_t` in *C*. + clonglong* {.importc: "long long", nodecl.} = int64 + ## This is the same as the type `long long` in *C*. + cfloat* {.importc: "float", nodecl.} = float32 + ## This is the same as the type `float` in *C*. + cdouble* {.importc: "double", nodecl.} = float64 + ## This is the same as the type `double` in *C*. + clongdouble* {.importc: "long double", nodecl.} = BiggestFloat + ## This is the same as the type `long double` in *C*. + ## This C type is not supported by Nim's code generator. + + cuchar* {.importc: "unsigned char", nodecl, deprecated: "use `char` or `uint8` instead".} = char + ## Deprecated: Use `uint8` instead. + cushort* {.importc: "unsigned short", nodecl.} = uint16 + ## This is the same as the type `unsigned short` in *C*. + cuint* {.importc: "unsigned int", nodecl.} = uint32 + ## This is the same as the type `unsigned int` in *C*. + culonglong* {.importc: "unsigned long long", nodecl.} = uint64 + ## This is the same as the type `unsigned long long` in *C*. + +type + ByteAddress* = int + ## is the signed integer type that should be used for converting + ## pointers to integer addresses for readability. + + cstringArray* {.importc: "char**", nodecl.} = ptr UncheckedArray[cstring] + ## This is binary compatible to the type `char**` in *C*. The array's + ## high value is large enough to disable bounds checking in practice. + ## Use `cstringArrayToSeq proc <#cstringArrayToSeq,cstringArray,Natural>`_ + ## to convert it into a `seq[string]`. + +when not defined(nimPreviewSlimSystem): + # pollutes namespace + type + PFloat32* {.deprecated: "use `ptr float32`".} = ptr float32 + ## An alias for `ptr float32`. + PFloat64* {.deprecated: "use `ptr float64`".} = ptr float64 + ## An alias for `ptr float64`. + PInt64* {.deprecated: "use `ptr int64`".} = ptr int64 + ## An alias for `ptr int64`. + PInt32* {.deprecated: "use `ptr int32`".} = ptr int32 + ## An alias for `ptr int32`. diff --git a/lib/system/dollars.nim b/lib/system/dollars.nim index 46085d2aa688..4ff3d0ae6cf5 100644 --- a/lib/system/dollars.nim +++ b/lib/system/dollars.nim @@ -3,9 +3,15 @@ runnableExamples: assert $0.1 == "0.1" assert $(-2*3) == "-6" -import std/private/digitsutils -import system/formatfloat -export addFloat +import std/private/[digitsutils, miscdollars] + +when not defined(nimPreviewSlimSystem): + import std/formatfloat + export addFloat + + func `$`*(x: float | float32): string = + ## Outplace version of `addFloat`. + result.addFloat(x) proc `$`*(x: int): string {.raises: [].} = ## Outplace version of `addInt`. @@ -27,9 +33,6 @@ gen(int) gen(uint64) gen(int64) -func `$`*(x: float | float32): string = - ## Outplace version of `addFloat`. - result.addFloat(x) proc `$`*(x: bool): string {.magic: "BoolToStr", noSideEffect.} ## The stringify operator for a boolean argument. Returns `x` @@ -69,24 +72,7 @@ proc `$`*(t: typedesc): string {.magic: "TypeTrait".} ## doAssert $(typeof("Foo")) == "string" ## static: doAssert $(typeof(@['A', 'B'])) == "seq[char]" -when defined(nimHasIsNamedTuple): - proc isNamedTuple(T: typedesc): bool {.magic: "TypeTrait".} -else: - # for bootstrap; remove after release 1.2 - proc isNamedTuple(T: typedesc): bool = - # Taken from typetraits. - when T isnot tuple: result = false - else: - var t: T - for name, _ in t.fieldPairs: - when name == "Field0": - return compiles(t.Field0) - else: - return true - return false - - -proc `$`*[T: tuple|object](x: T): string = +proc `$`*[T: tuple](x: T): string = ## Generic `$` operator for tuples that is lifted from the components ## of `x`. Example: ## @@ -94,28 +80,11 @@ proc `$`*[T: tuple|object](x: T): string = ## $(23, 45) == "(23, 45)" ## $(a: 23, b: 45) == "(a: 23, b: 45)" ## $() == "()" - result = "(" - const isNamed = T is object or isNamedTuple(T) - var count {.used.} = 0 - for name, value in fieldPairs(x): - if count > 0: result.add(", ") - when isNamed: - result.add(name) - result.add(": ") - count.inc - when compiles($value): - when value isnot string and value isnot seq and compiles(value.isNil): - if value.isNil: result.add "nil" - else: result.addQuoted(value) - else: - result.addQuoted(value) - else: - result.add("...") - when not isNamed: - if count == 1: - result.add(",") # $(1,) should print as the semantically legal (1,) - result.add(")") + tupleObjectDollar(result, x) +when not defined(nimPreviewSlimSystem): + import std/objectdollar + export objectdollar proc collectionToString[T](x: T, prefix, separator, suffix: string): string = result = prefix diff --git a/lib/system/exceptions.nim b/lib/system/exceptions.nim index 5dcd77bd0be8..63588f8589f1 100644 --- a/lib/system/exceptions.nim +++ b/lib/system/exceptions.nim @@ -1,61 +1,13 @@ -const NimStackTraceMsgs = - when defined(nimHasStacktraceMsgs): compileOption("stacktraceMsgs") - else: false +## Exception and effect types used in Nim code. type - RootEffect* {.compilerproc.} = object of RootObj ## \ - ## Base effect class. - ## - ## Each effect should inherit from `RootEffect` unless you know what - ## you're doing. TimeEffect* = object of RootEffect ## Time effect. IOEffect* = object of RootEffect ## IO effect. ReadIOEffect* = object of IOEffect ## Effect describing a read IO operation. WriteIOEffect* = object of IOEffect ## Effect describing a write IO operation. ExecIOEffect* = object of IOEffect ## Effect describing an executing IO operation. - StackTraceEntry* = object ## In debug mode exceptions store the stack trace that led - ## to them. A `StackTraceEntry` is a single entry of the - ## stack trace. - procname*: cstring ## Name of the proc that is currently executing. - line*: int ## Line number of the proc that is currently executing. - filename*: cstring ## Filename of the proc that is currently executing. - when NimStackTraceMsgs: - frameMsg*: string ## When a stacktrace is generated in a given frame and - ## rendered at a later time, we should ensure the stacktrace - ## data isn't invalidated; any pointer into PFrame is - ## subject to being invalidated so shouldn't be stored. - when defined(nimStackTraceOverride): - programCounter*: uint ## Program counter - will be used to get the rest of the info, - ## when `$` is called on this type. We can't use - ## "cuintptr_t" in here. - procnameStr*, filenameStr*: string ## GC-ed alternatives to "procname" and "filename" - - Exception* {.compilerproc, magic: "Exception".} = object of RootObj ## \ - ## Base exception class. - ## - ## Each exception has to inherit from `Exception`. See the full `exception - ## hierarchy `_. - parent*: ref Exception ## Parent exception (can be used as a stack). - name*: cstring ## The exception's name is its Nim identifier. - ## This field is filled automatically in the - ## `raise` statement. - msg* {.exportc: "message".}: string ## The exception's message. Not - ## providing an exception message - ## is bad style. - when defined(js): - trace: string - else: - trace: seq[StackTraceEntry] - up: ref Exception # used for stacking exceptions. Not exported! - - Defect* = object of Exception ## \ - ## Abstract base class for all exceptions that Nim's runtime raises - ## but that are strictly uncatchable as they can also be mapped to - ## a `quit` / `trap` / `exit` operation. - - CatchableError* = object of Exception ## \ - ## Abstract class for all exceptions that are catchable. +type IOError* = object of CatchableError ## \ ## Raised if an IO error occurred. EOFError* = object of IOError ## \ @@ -144,25 +96,27 @@ type ## ## This is only raised if the `segfaults module `_ was imported! - ArithmeticError* {.deprecated: "See corresponding Defect".} = ArithmeticDefect - DivByZeroError* {.deprecated: "See corresponding Defect".} = DivByZeroDefect - OverflowError* {.deprecated: "See corresponding Defect".} = OverflowDefect - AccessViolationError* {.deprecated: "See corresponding Defect".} = AccessViolationDefect - AssertionError* {.deprecated: "See corresponding Defect".} = AssertionDefect - OutOfMemError* {.deprecated: "See corresponding Defect".} = OutOfMemDefect - IndexError* {.deprecated: "See corresponding Defect".} = IndexDefect +when not defined(nimPreviewSlimSystem): + type + ArithmeticError* {.deprecated: "See corresponding Defect".} = ArithmeticDefect + DivByZeroError* {.deprecated: "See corresponding Defect".} = DivByZeroDefect + OverflowError* {.deprecated: "See corresponding Defect".} = OverflowDefect + AccessViolationError* {.deprecated: "See corresponding Defect".} = AccessViolationDefect + AssertionError* {.deprecated: "See corresponding Defect".} = AssertionDefect + OutOfMemError* {.deprecated: "See corresponding Defect".} = OutOfMemDefect + IndexError* {.deprecated: "See corresponding Defect".} = IndexDefect - FieldError* {.deprecated: "See corresponding Defect".} = FieldDefect - RangeError* {.deprecated: "See corresponding Defect".} = RangeDefect - StackOverflowError* {.deprecated: "See corresponding Defect".} = StackOverflowDefect - ReraiseError* {.deprecated: "See corresponding Defect".} = ReraiseDefect - ObjectAssignmentError* {.deprecated: "See corresponding Defect".} = ObjectAssignmentDefect - ObjectConversionError* {.deprecated: "See corresponding Defect".} = ObjectConversionDefect - FloatingPointError* {.deprecated: "See corresponding Defect".} = FloatingPointDefect - FloatInvalidOpError* {.deprecated: "See corresponding Defect".} = FloatInvalidOpDefect - FloatDivByZeroError* {.deprecated: "See corresponding Defect".} = FloatDivByZeroDefect - FloatOverflowError* {.deprecated: "See corresponding Defect".} = FloatOverflowDefect - FloatUnderflowError* {.deprecated: "See corresponding Defect".} = FloatUnderflowDefect - FloatInexactError* {.deprecated: "See corresponding Defect".} = FloatInexactDefect - DeadThreadError* {.deprecated: "See corresponding Defect".} = DeadThreadDefect - NilAccessError* {.deprecated: "See corresponding Defect".} = NilAccessDefect + FieldError* {.deprecated: "See corresponding Defect".} = FieldDefect + RangeError* {.deprecated: "See corresponding Defect".} = RangeDefect + StackOverflowError* {.deprecated: "See corresponding Defect".} = StackOverflowDefect + ReraiseError* {.deprecated: "See corresponding Defect".} = ReraiseDefect + ObjectAssignmentError* {.deprecated: "See corresponding Defect".} = ObjectAssignmentDefect + ObjectConversionError* {.deprecated: "See corresponding Defect".} = ObjectConversionDefect + FloatingPointError* {.deprecated: "See corresponding Defect".} = FloatingPointDefect + FloatInvalidOpError* {.deprecated: "See corresponding Defect".} = FloatInvalidOpDefect + FloatDivByZeroError* {.deprecated: "See corresponding Defect".} = FloatDivByZeroDefect + FloatOverflowError* {.deprecated: "See corresponding Defect".} = FloatOverflowDefect + FloatUnderflowError* {.deprecated: "See corresponding Defect".} = FloatUnderflowDefect + FloatInexactError* {.deprecated: "See corresponding Defect".} = FloatInexactDefect + DeadThreadError* {.deprecated: "See corresponding Defect".} = DeadThreadDefect + NilAccessError* {.deprecated: "See corresponding Defect".} = NilAccessDefect diff --git a/lib/system/formatfloat.nim b/lib/system/formatfloat.nim index aada3e1bf061..70dd857d5ad1 100644 --- a/lib/system/formatfloat.nim +++ b/lib/system/formatfloat.nim @@ -1,135 +1,6 @@ -# -# -# Nim's Runtime Library -# (c) Copyright 2019 Nim contributors -# -# See the file "copying.txt", included in this -# distribution, for details about the copyright. -# - -when defined(nimPreviewSlimSystem): - import std/assertions - -proc c_memcpy(a, b: pointer, size: csize_t): pointer {.importc: "memcpy", header: "", discardable.} - -proc addCstringN(result: var string, buf: cstring; buflen: int) = - # no nimvm support needed, so it doesn't need to be fast here either - let oldLen = result.len - let newLen = oldLen + buflen - result.setLen newLen - c_memcpy(result[oldLen].addr, buf, buflen.csize_t) - -import dragonbox, schubfach - -proc writeFloatToBufferRoundtrip*(buf: var array[65, char]; value: BiggestFloat): int = - ## This is the implementation to format floats. - ## - ## returns the amount of bytes written to `buf` not counting the - ## terminating '\0' character. - result = toChars(buf, value, forceTrailingDotZero=true) - buf[result] = '\0' - -proc writeFloatToBufferRoundtrip*(buf: var array[65, char]; value: float32): int = - result = float32ToChars(buf, value, forceTrailingDotZero=true) - buf[result] = '\0' - -proc c_sprintf(buf, frmt: cstring): cint {.header: "", - importc: "sprintf", varargs, noSideEffect.} - -proc writeToBuffer(buf: var array[65, char]; value: cstring) = - var i = 0 - while value[i] != '\0': - buf[i] = value[i] - inc i - -proc writeFloatToBufferSprintf*(buf: var array[65, char]; value: BiggestFloat): int = - ## This is the implementation to format floats. - ## - ## returns the amount of bytes written to `buf` not counting the - ## terminating '\0' character. - var n: int = c_sprintf(addr buf, "%.16g", value) - var hasDot = false - for i in 0..n-1: - if buf[i] == ',': - buf[i] = '.' - hasDot = true - elif buf[i] in {'a'..'z', 'A'..'Z', '.'}: - hasDot = true - if not hasDot: - buf[n] = '.' - buf[n+1] = '0' - buf[n+2] = '\0' - result = n + 2 - else: - result = n - # On Windows nice numbers like '1.#INF', '-1.#INF' or '1.#NAN' or 'nan(ind)' - # of '-1.#IND' are produced. - # We want to get rid of these here: - if buf[n-1] in {'n', 'N', 'D', 'd', ')'}: - writeToBuffer(buf, "nan") - result = 3 - elif buf[n-1] == 'F': - if buf[0] == '-': - writeToBuffer(buf, "-inf") - result = 4 - else: - writeToBuffer(buf, "inf") - result = 3 - -proc writeFloatToBuffer*(buf: var array[65, char]; value: BiggestFloat | float32): int {.inline.} = - when defined(nimPreviewFloatRoundtrip): - writeFloatToBufferRoundtrip(buf, value) - else: - writeFloatToBufferSprintf(buf, value) - -proc addFloatRoundtrip*(result: var string; x: float | float32) = - when nimvm: - doAssert false - else: - var buffer {.noinit.}: array[65, char] - let n = writeFloatToBufferRoundtrip(buffer, x) - result.addCstringN(cstring(buffer[0].addr), n) - -proc addFloatSprintf*(result: var string; x: float) = - when nimvm: - doAssert false - else: - var buffer {.noinit.}: array[65, char] - let n = writeFloatToBufferSprintf(buffer, x) - result.addCstringN(cstring(buffer[0].addr), n) - -proc nimFloatToString(a: float): cstring = - ## ensures the result doesn't print like an integer, i.e. return 2.0, not 2 - # print `-0.0` properly - asm """ - function nimOnlyDigitsOrMinus(n) { - return n.toString().match(/^-?\d+$/); - } - if (Number.isSafeInteger(`a`)) - `result` = `a` === 0 && 1 / `a` < 0 ? "-0.0" : `a`+".0" - else { - `result` = `a`+"" - if(nimOnlyDigitsOrMinus(`result`)){ - `result` = `a`+".0" - } - } - """ - -proc addFloat*(result: var string; x: float | float32) {.inline.} = - ## Converts float to its string representation and appends it to `result`. - runnableExamples: - var - s = "foo:" - b = 45.67 - s.addFloat(45.67) - assert s == "foo:45.67" - template impl = - when defined(nimPreviewFloatRoundtrip): - addFloatRoundtrip(result, x) - else: - addFloatSprintf(result, x) - when defined(js): - when nimvm: impl() - else: - result.add nimFloatToString(x) - else: impl() +when not defined(nimPreviewSlimSystem): + import std/formatfloat + export formatfloat + {.deprecated: "use `std/formatfloat`".} +else: + {.error: "use `std/formatfloat`".} diff --git a/lib/system/gc.nim b/lib/system/gc.nim index e50e80f11d1c..4ab76c05e276 100644 --- a/lib/system/gc.nim +++ b/lib/system/gc.nim @@ -561,35 +561,8 @@ proc growObj(old: pointer, newsize: int, gch: var GcHeap): pointer = gcTrace(res, csAllocated) track("growObj old", ol, 0) track("growObj new", res, newsize) - when defined(nimIncrSeqV3): - # since we steal the old seq's contents, we set the old length to 0. - cast[PGenericSeq](old).len = 0 - elif reallyDealloc: - sysAssert(allocInv(gch.region), "growObj before dealloc") - if ol.refcount shr rcShift <=% 1: - # free immediately to save space: - if (ol.refcount and ZctFlag) != 0: - var j = gch.zct.len-1 - var d = gch.zct.d - while j >= 0: - if d[j] == ol: - d[j] = res - break - dec(j) - beforeDealloc(gch, ol, "growObj stack trash") - decTypeSize(ol, ol.typ) - rawDealloc(gch.region, ol) - else: - # we split the old refcount in 2 parts. XXX This is still not entirely - # correct if the pointer that receives growObj's result is on the stack. - # A better fix would be to emit the location specific write barrier for - # 'growObj', but this is lots of more work and who knows what new problems - # this would create. - res.refcount = rcIncrement - decRef(ol) - else: - sysAssert(ol.typ != nil, "growObj: 5") - zeroMem(ol, sizeof(Cell)) + # since we steal the old seq's contents, we set the old length to 0. + cast[PGenericSeq](old).len = 0 when useCellIds: inc gch.idGenerator res.id = gch.idGenerator * 1000_000 + gch.gcThreadId diff --git a/lib/system/indices.nim b/lib/system/indices.nim new file mode 100644 index 000000000000..40e7419f6aef --- /dev/null +++ b/lib/system/indices.nim @@ -0,0 +1,156 @@ +type + BackwardsIndex* = distinct int ## Type that is constructed by `^` for + ## reversed array accesses. + ## (See `^ template <#^.t,int>`_) + +template `^`*(x: int): BackwardsIndex = BackwardsIndex(x) + ## Builtin `roof`:idx: operator that can be used for convenient array access. + ## `a[^x]` is a shortcut for `a[a.len-x]`. + ## + ## ``` + ## let + ## a = [1, 3, 5, 7, 9] + ## b = "abcdefgh" + ## + ## echo a[^1] # => 9 + ## echo b[^2] # => g + ## ``` + +proc `[]`*[T](s: openArray[T]; i: BackwardsIndex): T {.inline.} = + system.`[]`(s, s.len - int(i)) + +proc `[]`*[Idx, T](a: array[Idx, T]; i: BackwardsIndex): T {.inline.} = + a[Idx(a.len - int(i) + int low(a))] +proc `[]`*(s: string; i: BackwardsIndex): char {.inline.} = s[s.len - int(i)] + +proc `[]`*[T](s: var openArray[T]; i: BackwardsIndex): var T {.inline.} = + system.`[]`(s, s.len - int(i)) +proc `[]`*[Idx, T](a: var array[Idx, T]; i: BackwardsIndex): var T {.inline.} = + a[Idx(a.len - int(i) + int low(a))] +proc `[]`*(s: var string; i: BackwardsIndex): var char {.inline.} = s[s.len - int(i)] + +proc `[]=`*[T](s: var openArray[T]; i: BackwardsIndex; x: T) {.inline.} = + system.`[]=`(s, s.len - int(i), x) +proc `[]=`*[Idx, T](a: var array[Idx, T]; i: BackwardsIndex; x: T) {.inline.} = + a[Idx(a.len - int(i) + int low(a))] = x +proc `[]=`*(s: var string; i: BackwardsIndex; x: char) {.inline.} = + s[s.len - int(i)] = x + +template `..^`*(a, b: untyped): untyped = + ## A shortcut for `.. ^` to avoid the common gotcha that a space between + ## '..' and '^' is required. + a .. ^b + +template `..<`*(a, b: untyped): untyped = + ## A shortcut for `a .. pred(b)`. + ## ``` + ## for i in 5 ..< 9: + ## echo i # => 5; 6; 7; 8 + ## ``` + a .. (when b is BackwardsIndex: succ(b) else: pred(b)) + +template `[]`*(s: string; i: int): char = arrGet(s, i) +template `[]=`*(s: string; i: int; val: char) = arrPut(s, i, val) + +template `^^`(s, i: untyped): untyped = + (when i is BackwardsIndex: s.len - int(i) else: int(i)) + +template spliceImpl(s, a, L, b: untyped): untyped = + # make room for additional elements or cut: + var shift = b.len - max(0,L) # ignore negative slice size + var newLen = s.len + shift + if shift > 0: + # enlarge: + setLen(s, newLen) + for i in countdown(newLen-1, a+b.len): movingCopy(s[i], s[i-shift]) + else: + for i in countup(a+b.len, newLen-1): movingCopy(s[i], s[i-shift]) + # cut down: + setLen(s, newLen) + # fill the hole: + for i in 0 ..< b.len: s[a+i] = b[i] + +proc `[]`*[T, U: Ordinal](s: string, x: HSlice[T, U]): string {.inline.} = + ## Slice operation for strings. + ## Returns the inclusive range `[s[x.a], s[x.b]]`: + ## ``` + ## var s = "abcdef" + ## assert s[1..3] == "bcd" + ## ``` + let a = s ^^ x.a + let L = (s ^^ x.b) - a + 1 + result = newString(L) + for i in 0 ..< L: result[i] = s[i + a] + +proc `[]=`*[T, U: Ordinal](s: var string, x: HSlice[T, U], b: string) = + ## Slice assignment for strings. + ## + ## If `b.len` is not exactly the number of elements that are referred to + ## by `x`, a `splice`:idx: is performed: + ## + runnableExamples: + var s = "abcdefgh" + s[1 .. ^2] = "xyz" + assert s == "axyzh" + + var a = s ^^ x.a + var L = (s ^^ x.b) - a + 1 + if L == b.len: + for i in 0.. 0: var spaces = " " diff --git a/lib/system/orc.nim b/lib/system/orc.nim index 4c23aea6c61b..32c6b9adc49e 100644 --- a/lib/system/orc.nim +++ b/lib/system/orc.nim @@ -8,7 +8,7 @@ # # Cycle collector based on -# https://researcher.watson.ibm.com/researcher/files/us-bacon/Bacon01Concurrent.pdf +# https://www.cs.purdue.edu/homes/hosking/690M/Bacon01Concurrent.pdf # And ideas from Lins' in 2008 by the notion of "critical links", see # "Cyclic reference counting" by Rafael Dueire Lins # R.D. Lins / Information Processing Letters 109 (2008) 71–78 diff --git a/lib/system/repr_v2.nim b/lib/system/repr_v2.nim index 6ab5f3c3f744..0e9bec0f3b99 100644 --- a/lib/system/repr_v2.nim +++ b/lib/system/repr_v2.nim @@ -1,5 +1,8 @@ include system/inclrtl +when defined(nimPreviewSlimSystem): + import std/formatfloat + proc isNamedTuple(T: typedesc): bool {.magic: "TypeTrait".} ## imported from typetraits diff --git a/lib/system/reprjs.nim b/lib/system/reprjs.nim index 28935a4ef9fe..0818f9cc97be 100644 --- a/lib/system/reprjs.nim +++ b/lib/system/reprjs.nim @@ -8,6 +8,9 @@ # # The generic ``repr`` procedure for the javascript backend. +when defined(nimPreviewSlimSystem): + import std/formatfloat + proc reprInt(x: int64): string {.compilerproc.} = $x proc reprFloat(x: float): string {.compilerproc.} = $x @@ -115,7 +118,6 @@ proc reprArray(a: pointer, typ: PNimType, # We prepend @ to seq, the C backend prepends the pointer to the seq. result = if typ.kind == tySequence: "@[" else: "[" var len: int = 0 - var i: int = 0 {. emit: "`len` = `a`.length;\n" .} var dereffed: pointer = a diff --git a/lib/system/seqs_v2.nim b/lib/system/seqs_v2.nim index 1194f40efff7..42d9938c5534 100644 --- a/lib/system/seqs_v2.nim +++ b/lib/system/seqs_v2.nim @@ -129,3 +129,19 @@ proc setLen[T](s: var seq[T], newlen: Natural) = proc newSeq[T](s: var seq[T], len: Natural) = shrink(s, 0) setLen(s, len) + + +template capacityImpl(sek: NimSeqV2): int = + if sek.p != nil: sek.p.cap else: 0 + +func capacity*[T](self: seq[T]): int {.inline.} = + ## Returns the current capacity of the seq. + # See https://github.com/nim-lang/RFCs/issues/460 + runnableExamples: + var lst = newSeqOfCap[string](cap = 42) + lst.add "Nim" + assert lst.capacity == 42 + + {.cast(noSideEffect).}: + let sek = unsafeAddr self + result = capacityImpl(cast[ptr NimSeqV2](sek)[]) diff --git a/lib/system/strmantle.nim b/lib/system/strmantle.nim index 9cf4f9e55f75..feaac7817239 100644 --- a/lib/system/strmantle.nim +++ b/lib/system/strmantle.nim @@ -43,6 +43,29 @@ proc hashString(s: string): int {.compilerproc.} = h = h + h shl 15 result = cast[int](h) +proc eqCstrings(a, b: cstring): bool {.inline, compilerproc.} = + if pointer(a) == pointer(b): result = true + elif a.isNil or b.isNil: result = false + else: result = c_strcmp(a, b) == 0 + +proc hashCstring(s: cstring): int {.compilerproc.} = + # the compiler needs exactly the same hash function! + # this used to be used for efficient generation of cstring case statements + if s.isNil: return 0 + var h : uint = 0 + var i = 0 + while true: + let c = s[i] + if c == '\0': break + h = h + uint(c) + h = h + h shl 10 + h = h xor (h shr 6) + inc i + h = h + h shl 3 + h = h xor (h shr 11) + h = h + h shl 15 + result = cast[int](h) + proc c_strtod(buf: cstring, endptr: ptr cstring): float64 {. importc: "strtod", header: "", noSideEffect.} diff --git a/lib/system/strs_v2.nim b/lib/system/strs_v2.nim index 6944cdc589df..74b9e7cd9869 100644 --- a/lib/system/strs_v2.nim +++ b/lib/system/strs_v2.nim @@ -175,3 +175,19 @@ proc prepareMutation*(s: var string) {.inline.} = {.cast(noSideEffect).}: let s = unsafeAddr s nimPrepareStrMutationV2(cast[ptr NimStringV2](s)[]) + + +template capacityImpl(str: NimStringV2): int = + if str.p != nil: str.p.cap else: 0 + +func capacity*(self: string): int {.inline.} = + ## Returns the current capacity of the string. + # See https://github.com/nim-lang/RFCs/issues/460 + runnableExamples: + var str = newStringOfCap(cap = 42) + str.add "Nim" + assert str.capacity == 42 + + {.cast(noSideEffect).}: + let str = unsafeAddr self + result = capacityImpl(cast[ptr NimStringV2](str)[]) diff --git a/lib/system/sysstr.nim b/lib/system/sysstr.nim index 49fff41e9ee7..7655d900416b 100644 --- a/lib/system/sysstr.nim +++ b/lib/system/sysstr.nim @@ -160,13 +160,9 @@ proc addChar(s: NimString, c: char): NimString = result = s if result.len >= result.space: let r = resize(result.space) - when defined(nimIncrSeqV3): - result = rawNewStringNoInit(r) - result.len = s.len - copyMem(addr result.data[0], unsafeAddr(s.data[0]), s.len+1) - else: - result = cast[NimString](growObj(result, - sizeof(TGenericSeq) + r + 1)) + result = rawNewStringNoInit(r) + result.len = s.len + copyMem(addr result.data[0], unsafeAddr(s.data[0]), s.len+1) result.reserved = r result.data[result.len] = c result.data[result.len+1] = '\0' @@ -210,12 +206,9 @@ proc resizeString(dest: NimString, addlen: int): NimString {.compilerRtl.} = result = dest else: # slow path: let sp = max(resize(dest.space), dest.len + addlen) - when defined(nimIncrSeqV3): - result = rawNewStringNoInit(sp) - result.len = dest.len - copyMem(addr result.data[0], unsafeAddr(dest.data[0]), dest.len+1) - else: - result = cast[NimString](growObj(dest, sizeof(TGenericSeq) + sp + 1)) + result = rawNewStringNoInit(sp) + result.len = dest.len + copyMem(addr result.data[0], unsafeAddr(dest.data[0]), dest.len+1) result.reserved = sp #result = rawNewString(sp) #copyMem(result, dest, dest.len + sizeof(TGenericSeq)) @@ -239,14 +232,11 @@ proc setLengthStr(s: NimString, newLen: int): NimString {.compilerRtl.} = result = s else: let sp = max(resize(s.space), newLen) - when defined(nimIncrSeqV3): - result = rawNewStringNoInit(sp) - result.len = s.len - copyMem(addr result.data[0], unsafeAddr(s.data[0]), s.len+1) - zeroMem(addr result.data[s.len], newLen - s.len) - result.reserved = sp - else: - result = resizeString(s, n) + result = rawNewStringNoInit(sp) + result.len = s.len + copyMem(addr result.data[0], unsafeAddr(s.data[0]), s.len+1) + zeroMem(addr result.data[s.len], newLen - s.len) + result.reserved = sp result.len = n result.data[n] = '\0' @@ -282,15 +272,11 @@ proc incrSeqV3(s: PGenericSeq, typ: PNimType): PGenericSeq {.compilerproc.} = result = s if result.len >= result.space: let r = resize(result.space) - when defined(nimIncrSeqV3): - result = cast[PGenericSeq](newSeq(typ, r)) - result.len = s.len - copyMem(dataPointer(result, typ.base.align), dataPointer(s, typ.base.align), s.len * typ.base.size) - # since we steal the content from 's', it's crucial to set s's len to 0. - s.len = 0 - else: - result = cast[PGenericSeq](growObj(result, align(GenericSeqSize, typ.base.align) + typ.base.size * r)) - result.reserved = r + result = cast[PGenericSeq](newSeq(typ, r)) + result.len = s.len + copyMem(dataPointer(result, typ.base.align), dataPointer(s, typ.base.align), s.len * typ.base.size) + # since we steal the content from 's', it's crucial to set s's len to 0. + s.len = 0 proc setLengthSeq(seq: PGenericSeq, elemSize, elemAlign, newLen: int): PGenericSeq {. compilerRtl, inl.} = @@ -304,20 +290,10 @@ proc setLengthSeq(seq: PGenericSeq, elemSize, elemAlign, newLen: int): PGenericS when not defined(boehmGC) and not defined(nogc) and not defined(gcMarkAndSweep) and not defined(gogc) and not defined(gcRegions): - when false: # deadcode: was used by `compileOption("gc", "v2")` + if ntfNoRefs notin extGetCellType(result).base.flags: for i in newLen..result.len-1: - let len0 = gch.tempStack.len forAllChildrenAux(dataPointer(result, elemAlign, elemSize, i), - extGetCellType(result).base, waPush) - let len1 = gch.tempStack.len - for i in len0 ..< len1: - doDecRef(gch.tempStack.d[i], LocalHeap, MaybeCyclic) - gch.tempStack.len = len0 - else: - if ntfNoRefs notin extGetCellType(result).base.flags: - for i in newLen..result.len-1: - forAllChildrenAux(dataPointer(result, elemAlign, elemSize, i), - extGetCellType(result).base, waZctDecRef) + extGetCellType(result).base, waZctDecRef) # XXX: zeroing out the memory can still result in crashes if a wiped-out # cell is aliased by another pointer (ie proc parameter or a let variable). @@ -334,36 +310,33 @@ proc setLengthSeqV2(s: PGenericSeq, typ: PNimType, newLen: int): PGenericSeq {. if s == nil: result = cast[PGenericSeq](newSeq(typ, newLen)) else: - when defined(nimIncrSeqV3): - let elemSize = typ.base.size - let elemAlign = typ.base.align - if s.space < newLen: - let r = max(resize(s.space), newLen) - result = cast[PGenericSeq](newSeq(typ, r)) - copyMem(dataPointer(result, elemAlign), dataPointer(s, elemAlign), s.len * elemSize) - # since we steal the content from 's', it's crucial to set s's len to 0. - s.len = 0 - elif newLen < s.len: - result = s - # we need to decref here, otherwise the GC leaks! - when not defined(boehmGC) and not defined(nogc) and - not defined(gcMarkAndSweep) and not defined(gogc) and - not defined(gcRegions): - if ntfNoRefs notin typ.base.flags: - for i in newLen..result.len-1: - forAllChildrenAux(dataPointer(result, elemAlign, elemSize, i), - extGetCellType(result).base, waZctDecRef) - - # XXX: zeroing out the memory can still result in crashes if a wiped-out - # cell is aliased by another pointer (ie proc parameter or a let variable). - # This is a tough problem, because even if we don't zeroMem here, in the - # presence of user defined destructors, the user will expect the cell to be - # "destroyed" thus creating the same problem. We can destroy the cell in the - # finalizer of the sequence, but this makes destruction non-deterministic. - zeroMem(dataPointer(result, elemAlign, elemSize, newLen), (result.len-%newLen) *% elemSize) - else: - result = s - zeroMem(dataPointer(result, elemAlign, elemSize, result.len), (newLen-%result.len) *% elemSize) - result.len = newLen + let elemSize = typ.base.size + let elemAlign = typ.base.align + if s.space < newLen: + let r = max(resize(s.space), newLen) + result = cast[PGenericSeq](newSeq(typ, r)) + copyMem(dataPointer(result, elemAlign), dataPointer(s, elemAlign), s.len * elemSize) + # since we steal the content from 's', it's crucial to set s's len to 0. + s.len = 0 + elif newLen < s.len: + result = s + # we need to decref here, otherwise the GC leaks! + when not defined(boehmGC) and not defined(nogc) and + not defined(gcMarkAndSweep) and not defined(gogc) and + not defined(gcRegions): + if ntfNoRefs notin typ.base.flags: + for i in newLen..result.len-1: + forAllChildrenAux(dataPointer(result, elemAlign, elemSize, i), + extGetCellType(result).base, waZctDecRef) + + # XXX: zeroing out the memory can still result in crashes if a wiped-out + # cell is aliased by another pointer (ie proc parameter or a let variable). + # This is a tough problem, because even if we don't zeroMem here, in the + # presence of user defined destructors, the user will expect the cell to be + # "destroyed" thus creating the same problem. We can destroy the cell in the + # finalizer of the sequence, but this makes destruction non-deterministic. + zeroMem(dataPointer(result, elemAlign, elemSize, newLen), (result.len-%newLen) *% elemSize) else: - result = setLengthSeq(s, typ.base.size, newLen) + result = s + zeroMem(dataPointer(result, elemAlign, elemSize, result.len), (newLen-%result.len) *% elemSize) + result.len = newLen diff --git a/lib/system/threadlocalstorage.nim b/lib/system/threadlocalstorage.nim index cea9abb420c9..b62c903c34a8 100644 --- a/lib/system/threadlocalstorage.nim +++ b/lib/system/threadlocalstorage.nim @@ -141,7 +141,7 @@ else: header: "".} = cint else: type - SysThread* {.importc: "pthread_t", header: "".} = object + SysThread* {.importc: "pthread_t", header: "".} = int Pthread_attr {.importc: "pthread_attr_t", header: "".} = object ThreadVarSlot {.importc: "pthread_key_t", diff --git a/lib/system/threads.nim b/lib/system/threads.nim index 1c1d1ca1c04d..aaaa33bb72e0 100644 --- a/lib/system/threads.nim +++ b/lib/system/threads.nim @@ -47,6 +47,9 @@ when not declared(ThisIsSystem): {.error: "You must not import this module explicitly".} +when defined(nimPreviewSlimSystem): + import std/assertions + const hasAllocStack = defined(zephyr) # maybe freertos too? diff --git a/lib/system/widestrs.nim b/lib/system/widestrs.nim index 8b08959b5c02..bb348fd6776a 100644 --- a/lib/system/widestrs.nim +++ b/lib/system/widestrs.nim @@ -224,3 +224,6 @@ when defined(nimv2): proc `$`*(s: WideCStringObj): string = $(s.data) + + proc len*(w: WideCStringObj): int {.inline.} = + len(w.data) diff --git a/lib/system_overview.rst b/lib/system_overview.rst index 768fdcd6d82b..1580693753ec 100644 --- a/lib/system_overview.rst +++ b/lib/system_overview.rst @@ -5,9 +5,11 @@ The System module imports several separate modules, and their documentation is in separate files: * `iterators `_ +* `exceptions `_ * `assertions `_ * `dollars `_ * `widestrs `_ +* `ctypes `_ Here is a short overview of the most commonly used functions from the diff --git a/lib/windows/winlean.nim b/lib/windows/winlean.nim index cd59b68a409e..91b0d18c10ea 100644 --- a/lib/windows/winlean.nim +++ b/lib/windows/winlean.nim @@ -422,10 +422,10 @@ else: importc: "GetCommandLineA", stdcall, dynlib: "kernel32", sideEffect.} proc rdFileTime*(f: FILETIME): int64 = - result = ze64(f.dwLowDateTime) or (ze64(f.dwHighDateTime) shl 32) + result = int64(cast[uint32](f.dwLowDateTime)) or (int64(cast[uint32](f.dwHighDateTime)) shl 32) proc rdFileSize*(f: WIN32_FIND_DATA): int64 = - result = ze64(f.nFileSizeLow) or (ze64(f.nFileSizeHigh) shl 32) + result = int64(cast[uint32](f.nFileSizeLow)) or (int64(cast[uint32](f.nFileSizeHigh)) shl 32) proc getSystemTimeAsFileTime*(lpSystemTimeAsFileTime: var FILETIME) {. importc: "GetSystemTimeAsFileTime", dynlib: "kernel32", stdcall, sideEffect.} @@ -505,9 +505,9 @@ type Sockaddr_storage* {.importc: "SOCKADDR_STORAGE", header: "winsock2.h".} = object ss_family*: uint16 - ss_pad1: array[6, byte] - ss_align: int64 - ss_pad2: array[112, byte] + ss_pad1 {.importc: "__ss_pad1".}: array[6, byte] + ss_align {.importc: "__ss_align".}: int64 + ss_pad2 {.importc: "__ss_pad2".}: array[112, byte] Servent* = object s_name*: cstring diff --git a/lib/wrappers/openssl.nim b/lib/wrappers/openssl.nim index 00ec160cbc36..70fed664ef34 100644 --- a/lib/wrappers/openssl.nim +++ b/lib/wrappers/openssl.nim @@ -7,33 +7,40 @@ # distribution, for details about the copyright. # -## OpenSSL support +## OpenSSL wrapper. Supports OpenSSL >= 1.1.0 dynamically (as default) or statically linked +## using `--dynlibOverride:ssl`. ## -## When OpenSSL is dynamically linked, the wrapper provides partial forward and backward -## compatibility for OpenSSL versions above and below 1.1.0 -## -## OpenSSL can also be statically linked using `--dynlibOverride:ssl` for OpenSSL >= 1.1.0. -## If you want to statically link against OpenSSL 1.0.x, you now have to -## define the `openssl10` symbol via `-d:openssl10`. +## To use openSSL 3, either set `-d:sslVersion=3` or `-d:useOpenssl3`. ## ## Build and test examples: ## ## .. code-block:: +## ./bin/nim c -d:ssl -p:. -r tests/stdlib/tssl.nim +## ./bin/nim c -d:ssl --threads:on -p:. -r tests/stdlib/thttpclient_ssl.nim ## ./bin/nim c -d:ssl -p:. -r tests/untestable/tssl.nim ## ./bin/nim c -d:ssl -p:. --dynlibOverride:ssl --passl:-lcrypto --passl:-lssl -r tests/untestable/tssl.nim +## ./bin/nim r --putenv:NIM_TESTAMENT_REMOTE_NETWORKING:1 -d:ssl -p:testament/lib --threads:on tests/untestable/thttpclient_ssl_remotenetwork.nim + +# https://www.feistyduck.com/library/openssl-cookbook/online/ch-testing-with-openssl.html +# +from strutils import startsWith + +when defined(nimPreviewSlimSystem): + import std/syncio when defined(nimHasStyleChecks): {.push styleChecks: off.} const useWinVersion = defined(windows) or defined(nimdoc) -# To force openSSL version use -d:sslVersion=1.0.0 +# To force openSSL version use -d:sslVersion=1.2.3 # See: #10281, #10230 # General issue: # Other dynamic libraries (like libpg) load different openSSL version then what nim loads. # Having two different openSSL loaded version causes a crash. # Use this compile time define to force the openSSL version that your other dynamic libraries want. const sslVersion {.strdefine.}: string = "" +const useOpenssl3* {.booldefine.} = sslVersion.startsWith('3') when sslVersion != "": when defined(macosx): const @@ -52,7 +59,7 @@ when sslVersion != "": from posix import SocketHandle elif useWinVersion: - when defined(openssl10) or defined(nimOldDlls): + when defined(nimOldDlls): when defined(cpu64): const DLLSSLName* = "(ssleay32|ssleay64).dll" @@ -72,10 +79,11 @@ elif useWinVersion: from winlean import SocketHandle else: + # same list of versions but ordered differently? when defined(osx): - const versions = "(.1.1|.38|.39|.41|.43|.44|.45|.46|.47|.48|.10|.1.0.2|.1.0.1|.1.0.0|.0.9.9|.0.9.8|)" + const versions = "(.3|.1.1|.38|.39|.41|.43|.44|.45|.46|.47|.48|.10|.1.0.2|.1.0.1|.1.0.0|.0.9.9|.0.9.8|)" else: - const versions = "(.1.1|.1.0.2|.1.0.1|.1.0.0|.0.9.9|.0.9.8|.48|.47|.46|.45|.44|.43|.41|.39|.38|.10|)" + const versions = "(.3|.1.1|.1.0.2|.1.0.1|.1.0.0|.0.9.9|.0.9.8|.48|.47|.46|.45|.44|.43|.41|.39|.38|.10|)" when defined(macosx): const @@ -93,6 +101,9 @@ else: import dynlib +{.pragma: lcrypto, cdecl, dynlib: DLLUtilName, importc.} +{.pragma: lssl, cdecl, dynlib: DLLSSLName, importc.} + type SslStruct {.final, pure.} = object SslPtr* = ptr SslStruct @@ -102,7 +113,6 @@ type PSTACK* = SslPtr PX509* = SslPtr PX509_NAME* = SslPtr - PEVP_MD* = SslPtr PBIO_METHOD* = SslPtr BIO* = SslPtr EVP_PKEY* = SslPtr @@ -266,40 +276,29 @@ proc TLSv1_method*(): PSSL_METHOD{.cdecl, dynlib: DLLSSLName, importc.} # and support SSLv3, TLSv1, TLSv1.1 and TLSv1.2 # SSLv23_method(), SSLv23_server_method(), SSLv23_client_method() are removed in 1.1.0 -when compileOption("dynlibOverride", "ssl") or defined(noOpenSSLHacks): +when compileOption("dynlibOverride", "ssl"): # Static linking - - when defined(openssl10): - proc SSL_library_init*(): cint {.cdecl, dynlib: DLLSSLName, importc, discardable.} - proc SSL_load_error_strings*() {.cdecl, dynlib: DLLSSLName, importc.} - proc SSLv23_method*(): PSSL_METHOD {.cdecl, dynlib: DLLSSLName, importc.} - proc SSLeay(): culong {.cdecl, dynlib: DLLUtilName, importc.} - - proc getOpenSSLVersion*(): culong = - SSLeay() - else: + when not useOpenssl3: proc OPENSSL_init_ssl*(opts: uint64, settings: uint8): cint {.cdecl, dynlib: DLLSSLName, importc, discardable.} proc SSL_library_init*(): cint {.discardable.} = ## Initialize SSL using OPENSSL_init_ssl for OpenSSL >= 1.1.0 return OPENSSL_init_ssl(0.uint64, 0.uint8) - proc TLS_method*(): PSSL_METHOD {.cdecl, dynlib: DLLSSLName, importc.} - proc SSLv23_method*(): PSSL_METHOD = - TLS_method() + proc TLS_method*(): PSSL_METHOD {.cdecl, dynlib: DLLSSLName, importc.} - proc OpenSSL_version_num(): culong {.cdecl, dynlib: DLLUtilName, importc.} + proc OpenSSL_version_num(): culong {.cdecl, dynlib: DLLUtilName, importc.} - proc getOpenSSLVersion*(): culong = - ## Return OpenSSL version as unsigned long - OpenSSL_version_num() + proc getOpenSSLVersion*(): culong = + ## Return OpenSSL version as unsigned long + OpenSSL_version_num() - proc SSL_load_error_strings*() = - ## Removed from OpenSSL 1.1.0 - # This proc prevents breaking existing code calling SslLoadErrorStrings - # Static linking against OpenSSL < 1.1.0 is not supported - discard + proc SSL_load_error_strings*() = + ## Removed from OpenSSL 1.1.0 + # This proc prevents breaking existing code calling SslLoadErrorStrings + # Static linking against OpenSSL < 1.1.0 is not supported + discard - when defined(libressl) or defined(openssl10): + when defined(libressl): proc SSL_state(ssl: SslPtr): cint {.cdecl, dynlib: DLLSSLName, importc.} proc SSL_in_init*(ssl: SslPtr): cint {.inline.} = SSl_state(ssl) and SSL_ST_INIT @@ -309,12 +308,8 @@ when compileOption("dynlibOverride", "ssl") or defined(noOpenSSLHacks): template OpenSSL_add_all_algorithms*() = discard - proc SSLv23_client_method*(): PSSL_METHOD {.cdecl, dynlib: DLLSSLName, importc.} - proc SSLv2_method*(): PSSL_METHOD {.cdecl, dynlib: DLLSSLName, importc.} - proc SSLv3_method*(): PSSL_METHOD {.cdecl, dynlib: DLLSSLName, importc.} - else: - # Here we're trying to stay compatible with openssl 1.0.* and 1.1.*. Some + # Here we're trying to stay compatible with openssl 1.1.*. Some # symbols are loaded dynamically and we don't use them if not found. proc thisModule(): LibHandle {.inline.} = var thisMod {.global.}: LibHandle @@ -322,9 +317,12 @@ else: result = thisMod - proc sslModule(): LibHandle {.inline.} = + proc sslModule(): LibHandle {.inline, raises: [LibraryError], tags:[RootEffect].} = var sslMod {.global.}: LibHandle - if sslMod.isNil: sslMod = loadLibPattern(DLLSSLName) + try: + if sslMod.isNil: sslMod = loadLibPattern(DLLSSLName) + except: + raise newException(LibraryError, "Could not load SSL using " & DLLSSLName) result = sslMod @@ -349,63 +347,49 @@ else: if result.isNil and alternativeName.len > 0: result = symAddr(thisDynlib, alternativeName) - proc sslSymNullable(name: string, alternativeName = ""): pointer = + proc sslSymNullable(name: string, alternativeName = ""): pointer {.raises: [LibraryError], tags:[RootEffect].} = sslModule().symNullable(name, alternativeName) - proc sslSymThrows(name: string, alternativeName = ""): pointer = + proc sslSymThrows(name: string, alternativeName = ""): pointer {.raises: [LibraryError].} = result = sslSymNullable(name, alternativeName) if result.isNil: raiseInvalidLibrary(name) proc utilSymNullable(name: string, alternativeName = ""): pointer = utilModule().symNullable(name, alternativeName) - proc loadPSSLMethod(method1, method2: string): PSSL_METHOD = + proc loadPSSLMethod(method1, method2: string): PSSL_METHOD {.raises: [LibraryError], tags:[RootEffect].} = ## Load from OpenSSL if available, otherwise ## let methodSym = sslSymNullable(method1, method2) if methodSym.isNil: raise newException(LibraryError, "Could not load " & method1 & " nor " & method2) - let method2Proc = cast[proc(): PSSL_METHOD {.cdecl, gcsafe.}](methodSym) + let method2Proc = cast[proc(): PSSL_METHOD {.cdecl, gcsafe, raises: [].}](methodSym) return method2Proc() - proc SSL_library_init*(): cint {.discardable.} = - ## Initialize SSL using OPENSSL_init_ssl for OpenSSL >= 1.1.0 otherwise - ## SSL_library_init - let newInitSym = sslSymNullable("OPENSSL_init_ssl") - if not newInitSym.isNil: - let newInitProc = - cast[proc(opts: uint64, settings: uint8): cint {.cdecl.}](newInitSym) - return newInitProc(0, 0) - let olderProc = cast[proc(): cint {.cdecl.}](sslSymThrows("SSL_library_init")) - if not olderProc.isNil: result = olderProc() + when not useOpenssl3: + proc SSL_library_init*(): cint {.discardable.} = + ## Initialize SSL using OPENSSL_init_ssl for OpenSSL >= 1.1.0 otherwise + ## SSL_library_init + let newInitSym = sslSymNullable("OPENSSL_init_ssl") + if not newInitSym.isNil: + let newInitProc = + cast[proc(opts: uint64, settings: uint8): cint {.cdecl.}](newInitSym) + return newInitProc(0, 0) + let olderProc = cast[proc(): cint {.cdecl.}](sslSymThrows("SSL_library_init")) + if not olderProc.isNil: result = olderProc() proc SSL_load_error_strings*() = # TODO: Are we ignoring this on purpose? SSL GitHub CI fails otherwise. let theProc = cast[proc() {.cdecl.}](sslSymNullable("SSL_load_error_strings")) if not theProc.isNil: theProc() - proc SSLv23_client_method*(): PSSL_METHOD = - loadPSSLMethod("SSLv23_client_method", "TLS_client_method") - - proc SSLv23_method*(): PSSL_METHOD = - loadPSSLMethod("SSLv23_method", "TLS_method") - - proc SSLv2_method*(): PSSL_METHOD = - loadPSSLMethod("SSLv2_method", "TLS_method") - proc SSLv3_method*(): PSSL_METHOD = loadPSSLMethod("SSLv3_method", "TLS_method") proc TLS_method*(): PSSL_METHOD = loadPSSLMethod("TLS_method", "SSLv23_method") - proc TLS_client_method*(): PSSL_METHOD = - loadPSSLMethod("TLS_client_method", "SSLv23_client_method") - - proc TLS_server_method*(): PSSL_METHOD = - loadPSSLMethod("TLS_server_method", "SSLv23_server_method") - proc OpenSSL_add_all_algorithms*() = # TODO: Are we ignoring this on purpose? SSL GitHub CI fails otherwise. let theProc = cast[proc() {.cdecl.}](sslSymNullable("OPENSSL_add_all_algorithms_conf")) @@ -441,6 +425,9 @@ else: proc ERR_load_BIO_strings*(){.cdecl, dynlib: DLLUtilName, importc.} +proc TLS_client_method*(): PSSL_METHOD {.cdecl, dynlib: DLLSSLName, importc.} + + proc SSL_new*(context: SslCtx): SslPtr{.cdecl, dynlib: DLLSSLName, importc.} proc SSL_free*(ssl: SslPtr){.cdecl, dynlib: DLLSSLName, importc.} proc SSL_get_SSL_CTX*(ssl: SslPtr): SslCtx {.cdecl, dynlib: DLLSSLName, importc.} @@ -684,48 +671,48 @@ proc RSA_free*(rsa: PRSA) {.cdecl, dynlib: DLLUtilName, importc.} proc RSA_size*(rsa: PRSA): cint {.cdecl, dynlib: DLLUtilName, importc.} # sha types -proc EVP_md_null*(): EVP_MD {.cdecl, importc.} -proc EVP_md2*(): EVP_MD {.cdecl, importc.} -proc EVP_md4*(): EVP_MD {.cdecl, importc.} -proc EVP_md5*(): EVP_MD {.cdecl, importc.} -proc EVP_sha*(): EVP_MD {.cdecl, importc.} -proc EVP_sha1*(): EVP_MD {.cdecl, importc.} -proc EVP_dss*(): EVP_MD {.cdecl, importc.} -proc EVP_dss1*(): EVP_MD {.cdecl, importc.} -proc EVP_ecdsa*(): EVP_MD {.cdecl, importc.} -proc EVP_sha224*(): EVP_MD {.cdecl, importc.} -proc EVP_sha256*(): EVP_MD {.cdecl, importc.} -proc EVP_sha384*(): EVP_MD {.cdecl, importc.} -proc EVP_sha512*(): EVP_MD {.cdecl, importc.} -proc EVP_mdc2*(): EVP_MD {.cdecl, importc.} -proc EVP_ripemd160*(): EVP_MD {.cdecl, importc.} -proc EVP_whirlpool*(): EVP_MD {.cdecl, importc.} -proc EVP_MD_size*(md: EVP_MD): cint {.cdecl, importc.} +proc EVP_md_null*(): EVP_MD {.lcrypto.} +proc EVP_md2*(): EVP_MD {.lcrypto.} +proc EVP_md4*(): EVP_MD {.lcrypto.} +proc EVP_md5*(): EVP_MD {.lcrypto.} +proc EVP_sha*(): EVP_MD {.lcrypto.} +proc EVP_sha1*(): EVP_MD {.lcrypto.} +proc EVP_dss*(): EVP_MD {.lcrypto.} +proc EVP_dss1*(): EVP_MD {.lcrypto.} +proc EVP_ecdsa*(): EVP_MD {.lcrypto.} +proc EVP_sha224*(): EVP_MD {.lcrypto.} +proc EVP_sha256*(): EVP_MD {.lcrypto.} +proc EVP_sha384*(): EVP_MD {.lcrypto.} +proc EVP_sha512*(): EVP_MD {.lcrypto.} +proc EVP_mdc2*(): EVP_MD {.lcrypto.} +proc EVP_ripemd160*(): EVP_MD {.lcrypto.} +proc EVP_whirlpool*(): EVP_MD {.lcrypto.} +proc EVP_MD_size*(md: EVP_MD): cint {.lcrypto.} # hmac functions -proc HMAC*(evp_md: EVP_MD; key: pointer; key_len: cint; d: cstring; n: csize_t; md: cstring; md_len: ptr cuint): cstring {.cdecl, importc.} +proc HMAC*(evp_md: EVP_MD; key: pointer; key_len: cint; d: cstring; n: csize_t; md: cstring; md_len: ptr cuint): cstring {.lcrypto.} # RSA key functions -proc PEM_read_bio_PrivateKey*(bp: BIO, x: ptr EVP_PKEY, cb: pointer, u: pointer): EVP_PKEY {.cdecl, importc.} -proc EVP_PKEY_free*(p: EVP_PKEY) {.cdecl, importc.} -proc EVP_DigestSignInit*(ctx: EVP_MD_CTX, pctx: ptr EVP_PKEY_CTX, typ: EVP_MD, e: ENGINE, pkey: EVP_PKEY): cint {.cdecl, importc.} -proc EVP_DigestInit_ex*(ctx: EVP_MD_CTX, typ: PEVP_MD, engine: SslPtr = nil): cint {.cdecl, importc.} -proc EVP_DigestUpdate*(ctx: EVP_MD_CTX, data: pointer, len: cuint): cint {.cdecl, importc.} -proc EVP_DigestFinal_ex*(ctx: EVP_MD_CTX, buffer: pointer, size: ptr cuint): cint {.cdecl, importc.} -proc EVP_DigestSignFinal*(ctx: EVP_MD_CTX, data: pointer, len: ptr csize_t): cint {.cdecl, importc.} -proc EVP_PKEY_CTX_new*(pkey: EVP_PKEY, e: ENGINE): EVP_PKEY_CTX {.cdecl, importc.} -proc EVP_PKEY_CTX_free*(pkeyCtx: EVP_PKEY_CTX) {.cdecl, importc.} -proc EVP_PKEY_sign_init*(c: EVP_PKEY_CTX): cint {.cdecl, importc.} +proc PEM_read_bio_PrivateKey*(bp: BIO, x: ptr EVP_PKEY, cb: pointer, u: pointer): EVP_PKEY {.lcrypto.} +proc EVP_PKEY_free*(p: EVP_PKEY) {.lcrypto.} +proc EVP_DigestSignInit*(ctx: EVP_MD_CTX, pctx: ptr EVP_PKEY_CTX, typ: EVP_MD, e: ENGINE, pkey: EVP_PKEY): cint {.lcrypto.} +proc EVP_DigestInit_ex*(ctx: EVP_MD_CTX, typ: EVP_MD, engine: SslPtr = nil): cint {.lcrypto.} +proc EVP_DigestUpdate*(ctx: EVP_MD_CTX, data: pointer, len: cuint): cint {.lcrypto.} +proc EVP_DigestFinal_ex*(ctx: EVP_MD_CTX, buffer: pointer, size: ptr cuint): cint {.lcrypto.} +proc EVP_DigestSignFinal*(ctx: EVP_MD_CTX, data: pointer, len: ptr csize_t): cint {.lcrypto.} +proc EVP_PKEY_CTX_new*(pkey: EVP_PKEY, e: ENGINE): EVP_PKEY_CTX {.lcrypto.} +proc EVP_PKEY_CTX_free*(pkeyCtx: EVP_PKEY_CTX) {.lcrypto.} +proc EVP_PKEY_sign_init*(c: EVP_PKEY_CTX): cint {.lcrypto.} when defined(macosx) or defined(windows): - proc EVP_MD_CTX_create*(): EVP_MD_CTX {.cdecl, importc.} - proc EVP_MD_CTX_destroy*(ctx: EVP_MD_CTX) {.cdecl, importc.} - proc EVP_MD_CTX_cleanup*(ctx: EVP_MD_CTX): cint {.cdecl, importc.} + proc EVP_MD_CTX_create*(): EVP_MD_CTX {.lcrypto.} + proc EVP_MD_CTX_destroy*(ctx: EVP_MD_CTX) {.lcrypto.} + proc EVP_MD_CTX_cleanup*(ctx: EVP_MD_CTX): cint {.lcrypto.} else: # some times you will need this instead: - proc EVP_MD_CTX_create*(): EVP_MD_CTX {.cdecl, importc: "EVP_MD_CTX_new".} - proc EVP_MD_CTX_destroy*(ctx: EVP_MD_CTX) {.cdecl, importc: "EVP_MD_CTX_free".} - proc EVP_MD_CTX_cleanup*(ctx: EVP_MD_CTX): cint {.cdecl, importc: "EVP_MD_CTX_cleanup".} + proc EVP_MD_CTX_create*(): EVP_MD_CTX {.cdecl, importc: "EVP_MD_CTX_new", dynlib: DLLUtilName.} + proc EVP_MD_CTX_destroy*(ctx: EVP_MD_CTX) {.cdecl, importc: "EVP_MD_CTX_free", dynlib: DLLUtilName.} + proc EVP_MD_CTX_cleanup*(ctx: EVP_MD_CTX): cint {.cdecl, importc: "EVP_MD_CTX_cleanup", dynlib: DLLUtilName.} # type @@ -802,8 +789,17 @@ when defined(nimHasStyleChecks): # On old openSSL version some of these symbols are not available when not defined(nimDisableCertificateValidation) and not defined(windows): - proc SSL_get_peer_certificate*(ssl: SslCtx): PX509{.cdecl, dynlib: DLLSSLName, - importc.} + # proc SSL_get_peer_certificate*(ssl: SslCtx): PX509 = + # loadPSSLMethod("SSL_get_peer_certificate", "SSL_get1_peer_certificate") + + when useOpenssl3: + proc SSL_get1_peer_certificate*(ssl: SslCtx): PX509 {.cdecl, dynlib: DLLSSLName, importc.} + proc SSL_get_peer_certificate*(ssl: SslCtx): PX509 = + SSL_get1_peer_certificate(ssl) + + else: + proc SSL_get_peer_certificate*(ssl: SslCtx): PX509 {.cdecl, dynlib: DLLSSLName, importc.} + proc X509_get_subject_name*(a: PX509): PX509_NAME{.cdecl, dynlib: DLLSSLName, importc.} diff --git a/nim.nimble b/nim.nimble new file mode 100644 index 000000000000..a629c0e04b3f --- /dev/null +++ b/nim.nimble @@ -0,0 +1,14 @@ +version = system.NimVersion +author = "Andreas Rumpf" +description = "Compiler package providing the compiler sources as a library." +license = "MIT" + +bin = @["compiler/nim", "nimsuggest/nimsuggest"] +skipFiles = @["azure-pipelines.yml" , "build_all.bat" , "build_all.sh" , "build_nimble.bat" , "build_nimble.sh" , "changelog.md" , "koch.nim.cfg" , "nimblemeta.json" , "readme.md" , "security.md" ] +skipDirs = @["build" , "changelogs" , "ci" , "csources_v1" , "drnim" , "nimdoc", "testament"] + +before install: + when defined(windows): + exec "./build_all.bat" + else: + exec "./build_all.sh" diff --git a/nimdoc/rst2html/expected/rst_examples.html b/nimdoc/rst2html/expected/rst_examples.html index 2b5218d9fcaa..9debd16ac021 100644 --- a/nimdoc/rst2html/expected/rst_examples.html +++ b/nimdoc/rst2html/expected/rst_examples.html @@ -1,12 +1,11 @@ - + - + - +Not a Nim Manual @@ -17,45 +16,42 @@ -Not a Nim Manual + - -
    -
    -

    Not a Nim Manual

    -
    +
    +
    +

    Not a Nim Manual

    +
    -
    - -     Dark Mode -
    - -
    - Search: -
    -
    - Group by: - -
    -
    -
    -
    - -

    + +
    + +

    Authors:Andreas Rumpf, Zahary Karadjov
    Authors:Andreas Rumpf, Zahary Karadjov
    Version:|nimversion|

    "Complexity" seems to be a lot like "energy": you can transfer it from the end-user to one/some of the other players, but the total amount seems to remain pretty much constant for a given task. -- Ran

    @@ -262,19 +258,17 @@

    IntroductionF2 without pipe

    not in table

    - +

    -
    -
    - + diff --git a/nimdoc/rsttester.nim b/nimdoc/rsttester.nim index daca3dfc7680..a0bdfca1e16f 100644 --- a/nimdoc/rsttester.nim +++ b/nimdoc/rsttester.nim @@ -1,9 +1,14 @@ +# To run this, cd to the git repo root, and run "nim r nimdoc/rsttester.nim". +# to change expected results (after carefully verifying everything), use -d:nimTestsNimdocFixup + import os, strutils from std/private/gitutils import diffFiles const baseDir = "nimdoc/rst2html" +const fixup = defined(nimTestsNimdocFixup) + var failures = 0 proc exec(cmd: string) = @@ -29,7 +34,7 @@ proc testRst2Html(fixup = false) = if failures == 0: removeDir(baseDir / "source/htmldocs") -testRst2Html(defined(fixup)) +testRst2Html(fixup) # Check for failures if failures > 0: quit($failures & " failures occurred.") diff --git a/nimdoc/test_doctype/expected/test_doctype.html b/nimdoc/test_doctype/expected/test_doctype.html new file mode 100644 index 000000000000..416541d4da66 --- /dev/null +++ b/nimdoc/test_doctype/expected/test_doctype.html @@ -0,0 +1,83 @@ + + + + + + + +nimdoc/test_doctype/test_doctype + + + + + + + + + + + + + + + + +
    +
    +

    nimdoc/test_doctype/test_doctype

    +
    +
    +
    + + +
    +
    +
    + Search: +
    +
    + Group by: + +
    + + +
    +
    + +
    + +

    Check

    +

    +text

    + +

    Check

    +

    text

    + +
    +
    + + +
    +
    + + + diff --git a/nimdoc/test_doctype/test_doctype.nim b/nimdoc/test_doctype/test_doctype.nim new file mode 100644 index 000000000000..28d35fc1e8ce --- /dev/null +++ b/nimdoc/test_doctype/test_doctype.nim @@ -0,0 +1,15 @@ +# Markdown (default) interpretes this as text + a Markdown code block: + +## Check +## ~~~~~ +## text +## ~~~~~ + +{.doctype: RST.} + +# Now RST interpretes this as 2 headings: + +## Check +## ~~~~~ +## text +## ~~~~~ diff --git a/nimdoc/test_out_index_dot_html/expected/index.html b/nimdoc/test_out_index_dot_html/expected/index.html index c6a116bd6770..50a18bdae9b3 100644 --- a/nimdoc/test_out_index_dot_html/expected/index.html +++ b/nimdoc/test_out_index_dot_html/expected/index.html @@ -1,12 +1,11 @@ - + - + - +nimdoc/test_out_index_dot_html/foo @@ -17,94 +16,90 @@ -nimdoc/test_out_index_dot_html/foo + - -
    -
    -

    nimdoc/test_out_index_dot_html/foo

    -
    +
    +
    +

    nimdoc/test_out_index_dot_html/foo

    +
    -
    - -     Dark Mode -
    - -
    - Search: -
    -
    - Group by: - -
    -
      -
    • - Procs -
        +
        + + +
        + +
        + Search: +
        +
        + Group by: + +
        + +
      +
    -
    -
    - -

    -
    -

    Procs

    -
    - -
    -
    -
    proc foo() {....raises: [], tags: [].}
    -
    - -I do foo - -
    + +
    + +

    +
    +

    Procs

    +
    +
    +
    +
    proc foo() {....raises: [], tags: [], forbids: [].}
    +
    + + I do foo + +
    -
    +
    +
    -
    -
    - + diff --git a/nimdoc/test_out_index_dot_html/expected/theindex.html b/nimdoc/test_out_index_dot_html/expected/theindex.html index 8ee62a3302da..00c81189eda4 100644 --- a/nimdoc/test_out_index_dot_html/expected/theindex.html +++ b/nimdoc/test_out_index_dot_html/expected/theindex.html @@ -1,12 +1,11 @@ - + - + - +Index @@ -17,31 +16,28 @@ -Index + - -
    -
    -

    Index

    - Modules: index.

    API symbols

    +
    +
    +

    Index

    + Modules: index.

    API symbols

    foo:
    -
    -
    - + diff --git a/nimdoc/tester.nim b/nimdoc/tester.nim index 32dce07099cf..ef82ae1b99e0 100644 --- a/nimdoc/tester.nim +++ b/nimdoc/tester.nim @@ -36,7 +36,7 @@ proc testNimDoc(prjDir, docsDir: string; switches: NimSwitches; fixup = false) = if nimBuildIndexSwitches != "": exec("$1 buildIndex $2" % [nimExe, nimBuildIndexSwitches]) - for expected in walkDirRec(prjDir / "expected/"): + for expected in walkDirRec(prjDir / "expected/", checkDir=true): let produced = expected.replace('\\', '/').replace("/expected/", "/$1/" % [docsDir]) if not fileExists(produced): echo "FAILURE: files not found: ", produced @@ -79,5 +79,14 @@ let "$1/$2" % [test2Dir, test2DocsDir]]) testNimDoc(test2Dir, test2DocsDir, test2Switches, fixup) +# Test `nim doc` on file with `{.doctype.}` pragma +let + test3PrjDir = "test_doctype" + test3PrjName = "test_doctype" + test3Dir = baseDir / test3PrjDir + test3DocsDir = "htmldocs" + test3Switches = NimSwitches(doc: @["$1/$2.nim" % [test3Dir, test3PrjName]]) +testNimDoc(test3Dir, test3DocsDir, test3Switches, fixup) + if failures > 0: quit "$# failures occurred; see note in nimdoc/tester.nim regarding -d:nimTestsNimdocFixup" % $failures diff --git a/nimdoc/testproject/expected/nimdoc.out.css b/nimdoc/testproject/expected/nimdoc.out.css index 0014cf196719..f924f5a36f71 100644 --- a/nimdoc/testproject/expected/nimdoc.out.css +++ b/nimdoc/testproject/expected/nimdoc.out.css @@ -78,63 +78,45 @@ Modified by Boyd Greenfield and narimiran --clipboard-image: var(--clipboard-image-normal); } -.theme-switch-wrapper { - display: flex; - align-items: center; -} - -.theme-switch-wrapper em { - margin-left: 10px; - font-size: 1rem; -} - -.theme-switch { - display: inline-block; - height: 22px; - position: relative; - width: 50px; -} - -.theme-switch input { - display: none; -} - -.slider { - background-color: #ccc; - bottom: 0; - cursor: pointer; - left: 0; - position: absolute; - right: 0; - top: 0; - transition: .4s; -} - -.slider:before { - background-color: #fff; - bottom: 4px; - content: ""; - height: 13px; - left: 4px; - position: absolute; - transition: .4s; - width: 13px; -} - -input:checked + .slider { - background-color: #66bb6a; -} - -input:checked + .slider:before { - transform: translateX(26px); -} - -.slider.round { - border-radius: 17px; +@media (prefers-color-scheme: dark) { + [data-theme="auto"] { + --primary-background: #171921; + --secondary-background: #1e202a; + --third-background: #2b2e3b; + --info-background: #008000; + --warning-background: #807000; + --error-background: #c03000; + --border: #0e1014; + --text: #fff; + --anchor: #8be9fd; + --anchor-focus: #8be9fd; + --input-focus: #8be9fd; + --strong: #bd93f9; + --hint: #7A7C85; + --nim-sprite-base64: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAARMAAABMCAYAAABOBlMuAAAACXBIWXMAAAsTAAALEwEAmpwYAAAFFmlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNS42LWMxNDggNzkuMTY0MDM2LCAyMDE5LzA4LzEzLTAxOjA2OjU3ICAgICAgICAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgeG1sbnM6cGhvdG9zaG9wPSJodHRwOi8vbnMuYWRvYmUuY29tL3Bob3Rvc2hvcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RFdnQ9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZUV2ZW50IyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgMjEuMCAoV2luZG93cykiIHhtcDpDcmVhdGVEYXRlPSIyMDE5LTEyLTAzVDAxOjE4OjIyKzAxOjAwIiB4bXA6TW9kaWZ5RGF0ZT0iMjAxOS0xMi0wM1QwMToyMDoxMCswMTowMCIgeG1wOk1ldGFkYXRhRGF0ZT0iMjAxOS0xMi0wM1QwMToyMDoxMCswMTowMCIgZGM6Zm9ybWF0PSJpbWFnZS9wbmciIHBob3Rvc2hvcDpDb2xvck1vZGU9IjMiIHBob3Rvc2hvcDpJQ0NQcm9maWxlPSJzUkdCIElFQzYxOTY2LTIuMSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDplZGViMzU3MC1iNmZjLWQyNDQtYTExZi0yMjc5YmY4NDNhYTAiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6ZWRlYjM1NzAtYjZmYy1kMjQ0LWExMWYtMjI3OWJmODQzYWEwIiB4bXBNTTpPcmlnaW5hbERvY3VtZW50SUQ9InhtcC5kaWQ6ZWRlYjM1NzAtYjZmYy1kMjQ0LWExMWYtMjI3OWJmODQzYWEwIj4gPHhtcE1NOkhpc3Rvcnk+IDxyZGY6U2VxPiA8cmRmOmxpIHN0RXZ0OmFjdGlvbj0iY3JlYXRlZCIgc3RFdnQ6aW5zdGFuY2VJRD0ieG1wLmlpZDplZGViMzU3MC1iNmZjLWQyNDQtYTExZi0yMjc5YmY4NDNhYTAiIHN0RXZ0OndoZW49IjIwMTktMTItMDNUMDE6MTg6MjIrMDE6MDAiIHN0RXZ0OnNvZnR3YXJlQWdlbnQ9IkFkb2JlIFBob3Rvc2hvcCAyMS4wIChXaW5kb3dzKSIvPiA8L3JkZjpTZXE+IDwveG1wTU06SGlzdG9yeT4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz4JZNR8AAAfG0lEQVR4nO2deViTZ7r/7yxkJaxJ2MK+GCBAMCwS1kgUFQSKK4XWWqsz1jpjp3b0tDP1V+eqU391fqfT/mpPPd20drTFDS0KFEVWJSGAEgLIZpAICBJACIRs549Rj1WILAkBfD/XlevySp68z/0S3+/7vPdzLyidTgcLkU2bd+z39/f/q1gshsrKSoJELFCa2iaEuU9K6kb+8uXxv54/fzE8L/eswNT2zCfQpjbAGKS8lPFKSEjIXiaTCSEhIeDj4xNnapsQ5j6rktZGp6UlfxIdzQVzCplmanvmG1hTG2BIAtlc26CgoDfT0tL2e3l5AQCAjY0NkMnk/a9s2k6rrKw8UV8n1JjYTIQ5RlAw14KzmL3xze1vfJyUuMJaq9UCFovFm9qu+YbBxcSPFUYkk8l2Q0NDsvo6ocrQx5+I8Ih4bz6f/0l8fHyKlZXV4/dRKBQwmcwwMpn8A4FAoPgHhH9bV1sxa488wZxoaycnJ/a9e/duCa5fkc3WvAiTI4Ib77p+XdqHG9anbfLy8gAAgLGxMdBpF+bjvzExqJj4scKI0dHRnwQHB++orq7+AgDeMuTxJ2Jl4rqU9PT0EwEBAUQCgTDuGAaDAampqYepVKpHUHDk325Ulw0a266YuFW+Gzdu/MDPz29jfn7+XgA4aOw5ESZP6kvpCXv3vnM8NiaSamVl+fj9BepGNDoGFRN7e/slcXFxO1xcXMDJyWnH7j//H/fi4uJdgutXmgw5z5O8smn7X9euXbvf29sbMBjMhONQKBRYWVlBbGzsbjMzM3JoOG+/sKKwy1h2rd/4elpGRsYuLy+vaDweD2w2Oy1h5ZrCvEunEaeeiVnMiabyl/F2/+X9P+8JDPQHHA5napMWBAYTk6DgSNuEhIS9DAYDAP7tq1i6dOkqOp3OWbNu0wens44emeoxA9lcWwKBYEMkEm2JRKIdHo+3QKFQWJ1Op8ZgMER3d/dVq1evTnFycpr0MSkUCsTExGzH4/Gk1LTME/39/TI0Go1FoVCg1WrVY2NjipGRkcGRkRH5dPwrEZHLXMPCwjJSUlIy3dzcfB+97+rqGhYSEpIOAIiYmBguN3zL77dt3uPh4W5qUxYUBhMTb2/vjeHh4cvR6P/dILK0tITIyEg7BweHr363/Z3Ampqaf1Zcu/zMKiVsyVJvMplsRyKR7IhEor2FhYUbhUJhJCYm2pFIJB6JRAIymQx4PB7QaDRoNBowMzMDJycnwOOn7icjEokQGxu7icFgbLp///7jFY1WqwWlUgkjIyOgUCgO7Ni5Rz48PCwfHh7uGRkZeaBQKOSjo6ODCoVCXlNVKn/6uCsT13FXrVr1emho6BYKhfLMnP7+/omrU9LPX8g+UThloxEMxqJFXjxESAyPQcSEExrLWLNmzW57e/txP/fw8ABHR8cdDAaDt3xF2ru9vb03sVgs0cbGxs/FxWVZUlISj0aj+dna2oKtrS1M5PcwJCgUCry8vODRrs84vPfoH6OjoyCXy6Gvr+/R6+CWrX9s7evrk/b19bWr1Wqli4sLZ8OGDe95eXmxUSjUuAd0cHDwjoqK2sYKXFIhvnldYYTTQpgU4/8+jyASCYDGoCd+ZkYYF8OICYezl8PhuOkbQyAQIDo62s/NzS2np6cHbGxsgEajAYFAAAwGA1gsFia6CE0NgUAABwcHsLe3B61WC2q1eo9WqwWNRgNKpRLUajUQiUSgUCh6zwGHwwGTydzo5+eXBQBnZu8MEJ5keHhYPqyYWMtHR0ZBpVIhYj9FUDONgOUvT12+du3avMDAQJjssdRqNWCxCyrEZdLodDoQi8Ulx44de628NL/V1Pa8iERE8l2dHB2CJvpcq9Nqbt1qKURWj1Njxld0ZGTkAW9v70kLCQC8sEIC8O/HKx8fn2gmk8kHgCk7pRFmzrWyAikASE1tx0Jj2uH0EZHL/N7YtuvT4OBgzmz4OBYSeDweIiMjt2S++vtMP1YYEmmJsCCY8mNOIJtr6+zsHBcZGXmIw+G4mZubG8m0hU9HRwcUFxe/KxQKTyDRsQjznSmJCS9+dVRERMTfQ0NDo2xtbfUGiSFMjtHRUaitrc3Jzc09kHvxVLmp7UFAmC6oZQkvrZLL5RJhReHtiQb5scKIXC7371FRUX90dnYGIpE4JR8Jgn40Gg20t7fXFxYWfnr9+vWjz8sdYi+Osh4vzgUBwZSgtu94V+fs7Hx7YGCgra6u7khLS0u2RCwYeTQgKmYFh8fj/f/g4OAldnZ2prR1wdPd3Q1CofBQSUnJkdLi3N8E93FCY6k+Pj48FxcXjlar1ZSWlh65VvYr4kREmDNg79+/D3FxcW5OTk5uXl5evNbW1tL0jK3ZXV1d1ykUintycvInoaGhdkj+gvGxs7MDPp+/m0AgWMQvS/lyeHhYTqPRPJycnIJSU1NZ3t7eW2g0Gly/fv2oWq1Gij0hzClQ/gHhpLS0tEM8Hm/7I8Ho7++HlpYWsLa2Bg8PDxOb+OKhUCigqakJ7t+/D25ubuDu7g4oFAp0Oh08ePAAvv7666TTWUdzTG0nAsKTYMU3ryuSU18+4+bmFrZo0SIOAICVlRUsXrx4zkakLnRIJBI8CgJ8MtdJp9NBZ2enqL29XWRC8xAQxgUNAHD+3L8KGhoaCp78ABES04JCoX4jJAAAAwMDUFtbe96YpRMQEKbL41DU5ubmko6Ojj2PSgggzD36+/vrb9y4cX425zzw93/8EBjon2is44+NjSkePBjqGRwc7G5v7xBV19w8U5B/3qgrr9+/uWtXUuKKD/TZ9MXh/066/OuFmunO8dGBQ98HBbGSp/t9U6LRaDXK0dHBoeFhuVzeL22/0yFqamopufjLqRJ933ssJi0tLSXV1dWHGAzGbuObOzs8ubqa71vZKpUKOjo6blwpOF8zm/Mu5cVkLlkSaswprAHAaVihgK7O7oSGxltvfXLon3nXK4RHT2cdN4pfKDCAlZyUuMJan02nTmczAaBmunPw4qI3cbnh0/36XICq0+lgcPABp7OrK629vUP5z8++LLh2XXD05L++yxrvC4/F5EZ12WBS8saLS5Ys2U2lUufUY45SqQSlUgkqlQrUavXj19jYGGg0GtBoNKDT6UCn05VotVq1TqfToFAojFar1eh0Og0Wi8XhcDgeGo1+/PhgZmYGOBwOsFgsmJmZ/eY1F+nt7YXa2trs2Z73wdCQBgCMHp1IJpHA09MdPD3dLRIS+OtKisvWvbP7vf2lZdePVFwzbHTwyMiI3hidkZFRUKvUYzOZ48HQkBIA5nWqBAqFAktLC7C0tADmIh88Pz4uMSyUk7hn776DV4tKPn/6d/lNxp1MJqsRCASf8vn8XdMpOjRTVCoVjI2NgUqlAq1WCyMjI9DX1wf379+Hvr6+/Q8ePOgdGRmRKxSKx0WLFAqFXKlUKnQ6nUar1arHq47mxwrD4/F4Eg6HI2GxWDwej7cgkUjWFAqFam5uTjU3N6eRyeQPLSwswNraGqysrIBAIDwWFywW+zja11Qi29LSclIikeSZZPJZBovBAI8XA8HBQR9kZZ3lR8cmvFZSlGe00p8IkwONRkNERBj4+i7a4+XpHv307/IbMakWlciXJbx0nMPh7Jqo0JGh0el0MDo6Cl1dXSCVSkEmk7177969W319fe1DQ0M9KpVKoVarlWq1WjndNhUPG3ApAWDcOxLTLwSDwWAOotFoDBaLxRMIBAsrKysne3t7Xzqd7k2n0/c4OzsDlUoFHA4364IyMDAATU1NxdWikhcq6tXKyhJezljPJZKI2eERS5cZeoWCMD2srCwhPX0tVzk2djiCG//GtfLLUoBxShB0dHTU3Lx580sLC4vtJBLJKMZoNBqQSqUglUqPdnR01PT09DT19/fLHjx40DM0NNQ72933GiSVGgB4JFQK+LfoSAGgnL04yppEIh2xtLS0t7GxcaFSqR7Ozs4fMRgMcHR0nJX8pJs3b54Ui8UXjT7RHIRMIkFK8irfwcEHPwQELUmqvYHUGJkLmJubw8YNa/i9vfffY/px3myQiDTPiEl9nVDDX576jaenZ7SnpyfLUJNrNBqQyWRw+/bt4x0dHTdkMlltV1dXw/XygjkdEv4wB0YOAK0AUM70C8HQ6fSzdDrdm0qlejg6OrLc3Ny2MBiMadWjfR4PHjyAmzdvZs/1v5MxoVAokJK8iicWS95k+nH+s0EiQhqpzQGoVFtYk5a87ba0XQAA34xbpagg/5zoT7s/OGNnZ8eaaYkBuVwOnZ2d5VKpVNTS0lLS2NhYWFVZ3Dujg5qQh6uY+ocvCAiKIPn4+Jz19PSMdnV15VCpVL6Dg4NBViw6nQ5EItHRpqamqzM+2DzHzo4O69amftLQeKsAZrDLgmBY/PyYsCIhfs+SiKUFE5Y8EwqFx11cXDihoaFTjjFAoVAwPDwMHR0dourq6jNCofDHhZqUVnvjmgIAcgAgJyg40mLRokX8kJCQjT4+PussLS1n1JPl7t27UFxcfHguB6mNjY2B7G4naNRTWyygUCjAYDGAx+PB0sICSCSi3vFYLBbCwjjA8vddBQtATKb7d3saBwc7IJPJBpsHjUGDGRYLJBIJLK0sAfucmyIGg4FFi3y8AwNZtycUk5KiS02vvf7WWQaDkejg4DApQwAeh3xDaWnpPoFAcPxFqnP6sEvgGf+A8Bx3d/cvIyIiNi1evHjT8wpNj8fAwACUlZW9P9dD5+/ckcFbf9gd2dcnn9LNAovF4inmZHtXNxdOdBR3+/JlS33pdP29wolEInA4weuiYxOy5vvuTkeHDHb+8c8xvb33Z3R9/N+Df+uIjYk02DwkEsna2trS1d/fNyGeF7uTyw1/7g3R3t4O2OxA/TVghULhcQqFQk1JSfmYSNR/5wD4d6EfgUBwvLS09IhUKhW9qAV5H9YjKQwJi6uvrKw8ERoamhkSEpKp7w7yJEqlEiQSyZmysrJv53qjdaVSCZdyTk+3qFMrAJRHRPLPN95qeifj5fU7mYt8JhyMRqMhMJDFdnF25gDAvBYTpXIMWlpay2fq/8m5mDcIABYGnEcGAGI/VlhBZWX1yZdSkz55OX0dV5+7w9bGGvz8mPrFpK62QskJjf2GTqd7x8bGbpnID4BCoUAmk0lLSkqOiESik2UleS/MakQflYKrXQDQxY1a3tTe3i6KiIjY5OXlxX7e9+rr6wsuXbr0t4ffn9OgMWjghMZQRcLp+8GulRVI/QPC37Wxtnal0ajJtjY2E451ZjiBra31vE9lR2PQQKFQaAAwo98Yi8Xq9fpPd56HO6rlvKWJv/PwcK+JilyCmajWMw6HAzs7+rMFpQOCIn6zHywSFvXm5eUdFAqFZ9Rq9bgHa2trq79w4cK+zz49cAARkmcpL81v/a/Dhz49d+7c3qqqqjyVSjXuOJ1OBxKJpDw3N/fA5V+zax6978cKw/sHhM/raMrnUVdboSy4fPWQSFSjd5yFBQWIRNKEd2IEw1J4JUd88WL+R51d3XrHWVDMnxUTa2tr1zXrNiUGsrmPf7DS4tymCxcu7Kuurs55+kKQSqVN586d23vs+8NHDXUCC5Wzp3/Iy8rKeruysvLM2Nhvo7VVKhXU1tYWnj17du/T7UOdnZ2D7OzsfGGB09raVi4S1RzXl0eFw+EAj8chYjKLVFffyOrq1C8mJBLpWTFRKBRyDofzC4vFWvXk+1ev/CLOzs7eKxAIslQqFeh0Oujp6enKzs7em/XTd7OayTqfKb56sT4rK+sPAoHg5KO/o0KhAKFQmHXy5MkdF3/5+TeZmctXpIXZ29v7zqVcKWNRX1epuXu3U/y8pEw0GmndOZt0dnXVDw0P6/W5oNHoZ30mQ0NDPb29vfvj4+Pf3rR5B/7od188XnEUXr4gDgmL+0NfX5/U19d3d3l5+YGfTnyDtLmcIhXXLsu4UcvfR6PRGGtra9eysrIjYrE45+kt4Fheou/69es/unnz5vm7d+/Wmsre2WRkZGTQ1DYg/JYGiUiTm1ugBAC9IfHPiEmDpFITE7fqJI/H27lmzZpDq5LWtz55t6wUXO3ihMYerK+vz2tpaUFaM0yT8tL81ujYle+TSCTrvEunBU9/voTLd92wYcPHVCqV39XVdXCu7+oYCp1O90Kc50Jk3I5+xVcv1jc3N5d4enpSMzIyvkpK3sh78nORsKg3++yPBS/q1q+hKCm61DSekERGJ3ikp6d/ERsbm1xVVXWwtbX1hRFtFAqFPMLMUyZsDyoQCI7LZDKIiIjwzczM/GpV0vro2TTsRSUqZoX3+vXrP1u9enXi0NAQiESirIdRtggIc5oJ40zq6uryGhoa8ry8vBJCQ0O9USjU94mrN7yWc+EnvaXb5gJMvxCMp6cnl0Kh2Le1tZVXXLs8L1LXefGrWRkZGZ/x+XyeUqkEkUh0vqenZ14HZyG8OEwoJjdrygd37NxTEBkZmWBtbQ3BwcEeKBTq+/UbX3/355Pfzlmn66qk9dGbN29+k8PhbCSRSNDZ2Snb9ae/HCkpKTksEhbN2QTD5NSX+Vu3bj0cHBzsjcFg4O7du1BWVvbNwxB9BIQ5j94I2Fu3bhXW19cDl8sFLBYLHA7Hg0wmf/e77e84ffXlPz6fLSMnQ2paZkJ4eHjmtm3b+B4eHvZkMhlQKBTY29s72dvbfxgUFJT8x7ffP1NRUfHjXErnZ/qFYKKjo7dt3rz5g8DAQPtH/XHa2tpqGhsbC55/BASEuYFeMblz505NTU3NgfDw8PcwGAygUCjw9fW1IJPJn/1130Hv0tLSI4WXL4hny9inYS+Osvbz80tgMpn8jIwMPovFch2vpoiDgwM4ODhwfH19OYsWLeJv3/Hu+cbGxquzXZz5aZYlvMRJT0/fFhkZue3JZmfd3d0gEolOIr4ShPmEXjFpkFRqXlrzSnFnZ+d7Tk5OjzNfXVxcICMjY6ezszNnVdL6vU8HWhmbgKAIkrOzMyc1NTXz0YU4maAuOp0OK1as4EVFRfGEQqHg1dfePHzr1q2rs71S8WOF4f38/BLS09M/iIyM5DxdxLq5uVlcVVU1bgVwBIS5il4xAQCQyWRigUBwJikpKe3JVGQcDgdLly7l2tranti0ecf7IpEoy9hbxX6sMDydTvdevXr1ltjY2F3u7u6AxT73FJ7B3Nwc4uLiwthsdphQKCzZkL7l0/r6+oKbNeVG90+EhMXZL1++fFtycvKHrq6uz4igUqmE5ubmEiTHCWG+8dwrUXD9imz9xtd/jIuLS7N5KpsTjUZDUFCQE4PB+F4oFGYmJW888Mv5k4UTHGpGxC9LYaenp78VEhKyxdHRESgUyoyOh0KhwNraGuLi4qIDAgKi6+rqyjekb/mHMSN6N6RvSdu+ffseNpsdZm09ftuW+vp6EIvFSB9hhHnHpG7rUqm0orW1tdXS0tLj6TIEaDQaaDQaxMfH811dXTl/3Xfw+JUrVz411J01cfWG6IiIiC07d+5McHNzs7ewMGyOFw6HAwcHB6BSqVx3d/fwz7/4rkAgEBwXCoUnHpZonDGrU9J5MTEx27du3Zrm4uKC0beaqq6u/ry+vj7XEPMiIMwmkxKTimuXZe/u+fCkp6fnexPdUfF4PPj7+1szGIydLi4unF1/+kvenTt3RG1tbRXTqfma8lIG39/fP/HVV19NZrFYHpMpzjQTzMzMwNPTE+Pp6Zng6emZ4Ofnl5CesfV8bW1tznQe3/wDwvFeXl7Rvr6+Ca+88kpaUFCQh74GXzqdDrq7u6GpqankRQmdR1hYTNrhUFVVlcXj8d6ysrKy0OfstLS0hPj4eC6Xy+U2NzeDRCI5/sa2XeX37t1rGhwc7BoYGJBN1P+FFbiE5OzszGaxWImvvvrqpoCAAKfp+ERmCpPJBCaTmcnhcDJLS0u/TE59+YxUKhXoi/lg+oVgrKysGJaWlna2trYeaWlpXDabvTMgIGDSfp2KiorzbW1tL0zoPMLCYtJX6uVfs2u++PKowMPDgz+ZIslEIhECAgKAxWJlajSazJ6eHmhra4PW1tZvtmz9o6Czs7O+r6+vfWxsbFir1WosLCzsV6xYkcnj8d7z9vaelmPV0Hh5eYGnp+f2mJiY7UVFRZ/HL0v5tru7+5ZGo1FisVg8Docj4fF4CxsbG1c+nx/m7e39sYeHB7i4uIC5ufmU6r4ODQ1BZWXlifkSrYuA8DRTumIrKytPent78728vCb9HRQKBVgsFhwcHIBOpwObzd4yNja2RaVSwdDQEHR1dcHo6CjQaDRwdXWdsWPV0KBQKPDw8AA7O7udERERO2tra2FgYACoVCo4OTkBjUYDMpkMeDz+8WuqaLVaaGxsbL19+/YzSX8ICPOFqYrJidDQ0AwvLy/e80c/CwaDARKJBI86BdJoNHB3dwe1Wj0nViL6IJPJwGQywdnZGZRKJRAIBDBUx8OBgQEoLS39BtkORpjPTJg1PB61N64pmpqarvb39xvUiLkuJE9CJpPBxsbGYEICANDZ2SlHgtQQ5jtTEhMAgLq6ulyJRFJvDGNeREZGRkAikRSUFuci2cEI85opi0l+7hmBWCzOeV6dToTJcfv27cHr168jxbgR5j1TFhMAgObm5hKZDNl0MAQtLS3Xzpw6hkS8Isx7piUmUqlUIBAIJuyjgzA5Ojs7QSKRINGuCAuCaYmJsKKw68qVK59KJJIu5HFneiiVSigqKjouEolOmtoWBARDMC0xAQC4+MvPJadOnXq3ra1N8yL0dDEkOp0OSktLy/Pz8w8+3d4CAWG+Mm0xAQA4fuy/jl+8ePGju3fvGsqeBY9Wq4XKysrWU6dOvX31yi8mKyyFgGBoZiQmAAD/79D+fadPn96PCMrz0el0UFVV1frtt9+mj9fiAgFhPjNjMQEAyMvLO3Ds2LE/tLS0INmuerh27Vr9999//xoiJAgLEYOEntbVVigB4PNNm3cMpqSkfMRms50McdyFgkqlgqKiovJTp069nZ97BhEShAWJQePYj373xdF1GzbLFQrFx6Ghob766ne8KNy7dw+KiopO5ubmfmTK4tsICMbG4EkxWT99d35l4rre/v7+D0NCQvh0Ot3QU8wL1Go1SKVSTX5+/sH8/PyDSP8bhIWOUTLsLuVklQcFR65pbGzcvnLlyvfc3NwsCASCMaaac+h0OhgaGoLq6uqaCxcu/OV01tGcTw7uM7VZCAhGx2jpug/vxAd58atzoqKitq1cuXKnvb29saabE+h0Oqiurpbm5eUdrK6uPlspuDrvY0hmO4YIhUIBGq1/X2CmNqFQKL3/79HomZ/z82xEowyy9zFr80zGDqPn/hdeviBmL47ad+fOnRsRERGbQkNDo62srIw97azT2dkJxcXFx0tKSo7Mdh8hY4LD4TDPH2U4MFjMc6tLmZmZzaj+Aw6H0/t9PB4PGCxmRudNJBL0ngeZTAI0Gj3jv+1szfM88Hic8cUEAKCmqlQOAN/ELU2qkEgkySwWK3HRokVcBoMxG9MbDZ1OB83NzdDU1FRQW1t7XiAQHJ+ovu18pbr6Rg6L5ZtoM0EhcUPT0tJW8tWRb0vQqIkvgKqqmhnVfrl2TfANXo+gjKlUio4OWc1M5sjOzjnQUH8rbqLPu3t6moaGhmfc+3q25tGHUqmECoEIUKbIrVkcEkONiIh4jcvlvu7s7OxLo9GmVe7QVCgUCujq6oKGhoaCioqKo9XV1WeM3YDMVPDik1gpyas+XrVyeaKXl8czjyANjbcgI/MNmkg49Q4ECPOH3NyC4RUr+M8IcHt7B1y9WlKRl3/5kElKnD1sfXEoJCzueEBAQGJYWFgGk8nk2djYAIFAgLm4pTw6Ogqjo6Mgl8vhxo0b50tLS4/U19fnLvS2FIWXfxEDQNLmLW9ueW1TxtchHDaQyWRTm4VgYkZHR6G+vhF+/NfP+y5e+vVjiVgwZpKVydOwF0dZW1lZOTGZTD6bzU4LCAiIptPp8HTDL1MwOjoKLS0tUFdXd1IsFudIpdKKgYGB7tloJTrX4MUnsVJTEj9etzY10dHRAQAAGm81wcsZW5CVyQInL69gNCGBjwcAGBx8ANnncypOnTr3H9nn/reD55wovvrQpyIHAHFUzIocGo3mQaPRfBwdHVlubm7bXF1dgcFgABqNNvruglwuh7t374JMJoOOjo7P79y5I+ru7m7q7e1tXQi7MzOh8PIv4pCw2DdaWtte37Au7aPIyCWAxWABjUbPif9HCMbjURtKiaQBfvr5zH9evlJ0uLQ4r/nJMXNiZTIRrMAlJAcHB18HBweWo6Mjy8rKajeJRAJLS0uwtLQECwsLoFAogMfjAYvFgpmZ2XNXMyqVCoaHh2FoaAiGh4cfvwYGBqCvrw+6u7vfvnfvXlNvb29rT09Pq0QsUM7S6c4rNqS/lrZ5U+YPRBKR9M7u9xwqBUUvtNAudH766XSLE8PR49ixE78/8tVnX403Zk7fUR46NUUAIPIPCMdTKJTdNjY2QKPRgE6nA51OB1tbWyCRSIDD4YBAIAAejwcCgfDYUajVakGlUoFarQadTvfY79HX1wf9/f0gl8tBLpfDvXv3HvXw+dxQPYYXMj+d+P7Mmzv+5OHr6/OJWq1GBHeB09TcUiKuq/coKS3/eqIx/wPkiIXC3w6YjAAAAABJRU5ErkJggg=="); + + --keyword: #ff79c6; + --identifier: #f8f8f2; + --comment: #6272a4; + --operator: #ff79c6; + --punctuation: #f8f8f2; + --other: #f8f8f2; + --escapeSequence: #bd93f9; + --number: #bd93f9; + --literal: #f1fa8c; + --program: #9090c0; + --option: #90b010; + --raw-data: #8be9fd; + + --clipboard-image-normal: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' style='color: lightgray' fill='none' viewBox='0 0 24 24' stroke='currentColor'%3E %3Cpath stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2' /%3E %3C/svg%3E"); + --clipboard-image-selected: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' style='color: lightgray' viewBox='0 0 20 20' fill='currentColor'%3E %3Cpath d='M8 3a1 1 0 011-1h2a1 1 0 110 2H9a1 1 0 01-1-1z' /%3E %3Cpath d='M6 3a2 2 0 00-2 2v11a2 2 0 002 2h8a2 2 0 002-2V5a2 2 0 00-2-2 3 3 0 01-3 3H9a3 3 0 01-3-3z' /%3E %3C/svg%3E"); + --clipboard-image: var(--clipboard-image-normal); + } } -.slider.round:before { - border-radius: 50%; +.theme-select-wrapper { + display: flex; + align-items: center; } html { @@ -159,24 +141,30 @@ body { padding: 0; box-sizing: border-box; } -.column, -.columns { +.column, .columns { width: 100%; float: left; box-sizing: border-box; - margin-left: 1%; -} + margin-left: 1%; } -.column:first-child, -.columns:first-child { +.column:first-child, .columns:first-child { margin-left: 0; } +.container .row { + display: flex; } + .three.columns { - width: 22%; + width: 25.0%; + height: 100vh; + position: sticky; + top: 0px; + overflow-y: auto; + padding: 2px; } .nine.columns { - width: 77.0%; } + width: 75.0%; + padding-left: 1.5em; } .twelve.columns { width: 100%; @@ -269,25 +257,26 @@ a.nimdoc { a.toc-backref { text-decoration: none; - color: var(--text); } + color: var(--text); +} a.link-seesrc { color: #607c9f; font-size: 0.9em; - font-style: italic; } + font-style: italic; +} -a:hover, -a:focus { +a:hover, a:focus { color: var(--anchor-focus); - text-decoration: underline; } + text-decoration: underline; +} a:hover span.Identifier { color: var(--anchor); } -sub, -sup { +sub, sup { position: relative; font-size: 75%; line-height: 0; @@ -314,8 +303,7 @@ img { background: transparent !important; box-shadow: none !important; } - a, - a:visited { + a, a:visited { text-decoration: underline; } a[href]:after { @@ -329,16 +317,14 @@ img { a[href^="#"]:after { content: ""; } - pre, - blockquote { + pre, blockquote { border: 1px solid #999; page-break-inside: avoid; } thead { display: table-header-group; } - tr, - img { + tr, img { page-break-inside: avoid; } img { @@ -353,22 +339,18 @@ img { h1.title { page-break-before: avoid; } - p, - h2, - h3 { + p, h2, h3 { orphans: 3; widows: 3; } - h2, - h3 { + h2, h3 { page-break-after: avoid; } } p { margin-top: 0.5em; - margin-bottom: 0.5em; -} + margin-bottom: 0.5em; } small { font-size: 85%; } @@ -376,8 +358,7 @@ small { strong { font-weight: 600; font-size: 0.95em; - color: var(--strong); -} + color: var(--strong); } em { font-style: italic; } @@ -398,8 +379,7 @@ h1.title { text-align: center; font-weight: 900; margin-top: 0.75em; - margin-bottom: 0em; -} + margin-bottom: 0em; } h2 { font-size: 1.3em; @@ -426,36 +406,29 @@ h6 { font-size: 1.1em; } -ul, -ol { +ul, ol { padding: 0; margin-top: 0.5em; margin-left: 0.75em; } -ul ul, -ul ol, -ol ol, -ol ul { +ul ul, ul ol, ol ol, ol ul { margin-bottom: 0; margin-left: 1.25em; } ul.simple > li { - list-style-type: circle; -} + list-style-type: circle; } ul.simple-boot li { - list-style-type: none; - margin-left: 0em; - margin-bottom: 0.5em; -} + list-style-type: none; + margin-left: 0em; + margin-bottom: 0.5em; } ol.simple > li, ul.simple > li { margin-bottom: 0.2em; margin-left: 0.4em } ul.simple.simple-toc > li { - margin-top: 1em; -} + margin-top: 1em; } ul.simple-toc { list-style: none; @@ -464,8 +437,7 @@ ul.simple-toc { margin-top: 1em; } ul.simple-toc > li { - list-style-type: none; -} + list-style-type: none; } ul.simple-toc-section { list-style-type: circle; @@ -475,12 +447,10 @@ ul.simple-toc-section { ul.nested-toc-section { list-style-type: circle; margin-left: -0.75em; - color: var(--text); -} + color: var(--text); } ul.nested-toc-section > li { - margin-left: 1.25em; -} + margin-left: 1.25em; } ol.arabic { @@ -527,7 +497,8 @@ hr.footnote { margin-top: 0.15em; } div.footnote-group { - margin-left: 1em; } + margin-left: 1em; +} div.footnote-label { display: inline-block; min-width: 1.7em; @@ -611,7 +582,7 @@ pre { border: 1px solid var(--border); -webkit-border-radius: 6px; -moz-border-radius: 6px; - border-radius: 6px; + border-radius: 6px; } .copyToClipBoardBtn { @@ -629,7 +600,7 @@ pre { .copyToClipBoard:hover .copyToClipBoardBtn { visibility: visible; -} +} .pre-scrollable { max-height: 340px; @@ -694,8 +665,8 @@ table th { font-weight: bold; } table th.docinfo-name { - background-color: transparent; - text-align: right; + background-color: transparent; + text-align: right; } table tr:hover { @@ -712,31 +683,31 @@ table.borderless td, table.borderless th { padding: 0 0.5em 0 0 !important; } .admonition { - padding: 0.3em; - background-color: var(--secondary-background); - border-left: 0.4em solid #7f7f84; - margin-bottom: 0.5em; - -webkit-box-shadow: 0 5px 8px -6px rgba(0,0,0,.2); - -moz-box-shadow: 0 5px 8px -6px rgba(0,0,0,.2); - box-shadow: 0 5px 8px -6px rgba(0,0,0,.2); + padding: 0.3em; + background-color: var(--secondary-background); + border-left: 0.4em solid #7f7f84; + margin-bottom: 0.5em; + -webkit-box-shadow: 0 5px 8px -6px rgba(0,0,0,.2); + -moz-box-shadow: 0 5px 8px -6px rgba(0,0,0,.2); + box-shadow: 0 5px 8px -6px rgba(0,0,0,.2); } .admonition-info { - border-color: var(--info-background); + border-color: var(--info-background); } .admonition-info-text { - color: var(--info-background); + color: var(--info-background); } .admonition-warning { - border-color: var(--warning-background); + border-color: var(--warning-background); } .admonition-warning-text { - color: var(--warning-background); + color: var(--warning-background); } .admonition-error { - border-color: var(--error-background); + border-color: var(--error-background); } .admonition-error-text { - color: var(--error-background); + color: var(--error-background); } .first { @@ -770,8 +741,7 @@ div.footer, div.header { font-size: smaller; } div.footer { - padding-top: 5em; -} + padding-top: 5em; } div.line-block { display: block; @@ -790,17 +760,14 @@ div.search_results { background-color: var(--third-background); margin: 3em; padding: 1em; - border: 1px solid #4d4d4d; -} + border: 1px solid #4d4d4d; } div#global-links ul { margin-left: 0; - list-style-type: none; -} + list-style-type: none; } div#global-links > simple-boot { - margin-left: 3em; -} + margin-left: 3em; } hr.docutils { width: 75%; } @@ -980,8 +947,7 @@ span.Directive { span.option { font-weight: bold; font-family: "Source Code Pro", Monaco, Menlo, Consolas, "Courier New", monospace; - color: var(--option); -} + color: var(--option); } span.Prompt { font-weight: bold; @@ -997,11 +963,10 @@ span.program { text-decoration: underline; text-decoration-color: var(--hint); text-decoration-thickness: 0.05em; - text-underline-offset: 0.15em; -} + text-underline-offset: 0.15em; } -span.Command, span.Rule, span.Hyperlink, span.Label, span.Reference, -span.Other { +span.Command, span.Rule, span.Hyperlink, +span.Label, span.Reference, span.Other { color: var(--other); } /* Pop type, const, proc, and iterator defs in nim def blocks */ @@ -1039,17 +1004,14 @@ span.pragmadots { border-radius: 4px; margin: 0 2px; cursor: pointer; - font-size: 0.8em; -} + font-size: 0.8em; } span.pragmadots:hover { - background-color: var(--hint); -} + background-color: var(--hint); } + span.pragmawrap { - display: none; -} + display: none; } span.attachedType { display: none; - visibility: hidden; -} + visibility: hidden; } diff --git a/nimdoc/testproject/expected/subdir/subdir_b/utils.html b/nimdoc/testproject/expected/subdir/subdir_b/utils.html index f94da7f40e51..d888a4609cbd 100644 --- a/nimdoc/testproject/expected/subdir/subdir_b/utils.html +++ b/nimdoc/testproject/expected/subdir/subdir_b/utils.html @@ -1,12 +1,11 @@ - + - + - +subdir/subdir_b/utils @@ -17,215 +16,193 @@ -subdir/subdir_b/utils + - -
    -
    -

    subdir/subdir_b/utils

    -
    +
    +
    +

    subdir/subdir_b/utils

    +
    -
    - -     Dark Mode -
    - -
    - Search: -
    -
    - Group by: - -
    - +
  • - Templates - + +
  • -
    -
    - -

    This is a description of the utils module.

    + +
    + +

    This is a description of the utils module.

    Links work:

    -
    -
    - + diff --git a/nimdoc/testproject/expected/subdir/subdir_b/utils.idx b/nimdoc/testproject/expected/subdir/subdir_b/utils.idx index 5c8fee5277ab..007101b374e5 100644 --- a/nimdoc/testproject/expected/subdir/subdir_b/utils.idx +++ b/nimdoc/testproject/expected/subdir/subdir_b/utils.idx @@ -36,3 +36,6 @@ Next header subdir/subdir_b/utils.html#this-is-now-a-header-next-header Next h And so on subdir/subdir_b/utils.html#next-header-and-so-on And so on More headers subdir/subdir_b/utils.html#more-headers More headers Up to level 6 subdir/subdir_b/utils.html#more-headers-up-to-level-6 Up to level 6 +Pandoc Markdown subdir/subdir_b/utils.html#pandoc-markdown Pandoc Markdown +Link name syntax subdir/subdir_b/utils.html#pandoc-markdown-link-name-syntax Link name syntax +Symbols documentation subdir/subdir_b/utils.html#pandoc-markdown-symbols-documentation Symbols documentation diff --git a/nimdoc/testproject/expected/testproject.html b/nimdoc/testproject/expected/testproject.html index cba9391afe06..ea0269710e03 100644 --- a/nimdoc/testproject/expected/testproject.html +++ b/nimdoc/testproject/expected/testproject.html @@ -1,12 +1,11 @@ - + - + - +testproject @@ -17,372 +16,323 @@ -testproject + - -
    -
    -

    testproject

    -
    +
    +
    +

    testproject

    +
    -
    - -     Dark Mode -
    - -
    - Search: -
    -
    - Group by: - -
    - +
  • - Iterators - + +
  • - Macros - + +
  • - Templates - + + + + + + + + +
  • -
    -
    - -

    This is the top level module. + +

    + +

    This is the top level module.

    Example:

    import testproject
     import subdir / subdir_b / utils
    @@ -396,133 +346,136 @@ 

    testproject

    Example:

    import testproject
     discard "in top3"
    top3 after

    - +
    -

    Types

    -
    -
    -
    A {.inject.} = enum
    +  

    Types

    +
    +
    +
    A {.inject.} = enum
       aA
    -
    - -The enum A. - -
    +
    + + The enum A. + +
    -
    B {.inject.} = enum
    +  
    B {.inject.} = enum
       bB
    -
    - -The enum B. - -
    +
    + + The enum B. + +
    -
    Foo = enum
    +  
    Foo = enum
       enumValueA2
    -
    - - - -
    +
    + + + +
    -
    FooBuzz {....deprecated: "FooBuzz msg".} = int
    -
    -
    - Deprecated: FooBuzz msg -
    - - +
    FooBuzz {....deprecated: "FooBuzz msg".} = int
    +
    +
    + Deprecated: FooBuzz msg +
    -
    + + +
    -
    Shapes = enum
    +  
    Shapes = enum
       Circle,                   ## A circle
       Triangle,                 ## A three-sided shape
       Rectangle                  ## A four-sided shape
    -
    - -Some shapes. - -
    +
    + + Some shapes. + +
    -
    +
    +
    -

    Vars

    -
    -
    -
    aVariable: array[1, int]
    -
    - - - -
    +

    Vars

    +
    +
    +
    aVariable: array[1, int]
    +
    + + + +
    -
    someVariable: bool
    -
    - -This should be visible. - -
    +
    someVariable: bool
    +
    + + This should be visible. + +
    -
    +
    +
    -

    Consts

    -
    -
    -
    C_A = 0x7FF0000000000000'f64
    -
    - - - -
    +

    Consts

    +
    +
    +
    C_A = 0x7FF0000000000000'f64
    +
    + + + +
    -
    C_B = 0o377'i8
    -
    - - - -
    +
    C_B = 0o377'i8
    +
    + + + +
    -
    C_C = 0o277'i8
    -
    - - - -
    +
    C_C = 0o277'i8
    +
    + + + +
    -
    C_D = 0o177777'i16
    -
    - - - -
    +
    C_D = 0o177777'i16
    +
    + + + +
    -
    +
    +
    -

    Procs

    -
    - -
    -
    -
    proc addfBug14485() {....raises: [], tags: [].}
    -
    - -Some proc +

    Procs

    +
    +
    +
    +
    proc addfBug14485() {....raises: [], tags: [], forbids: [].}
    +
    + + Some proc

    Example:

    discard "foo() = " & $[1]
     #[
    @@ -535,208 +488,197 @@ 

    Procs

    6: </script 7: end of broken html ]#
    - -
    + +
    -
    -
    -
    proc anything() {....raises: [], tags: [].}
    -
    - -There is no block quote after blank lines at the beginning. - -
    +
    +
    proc anything() {....raises: [], tags: [], forbids: [].}
    +
    + + There is no block quote after blank lines at the beginning. + +
    -
    -
    -
    proc asyncFun1(): Future[int] {....raises: [Exception, ValueError],
    -                                tags: [RootEffect].}
    -
    - -ok1 - -
    +
    +
    proc asyncFun1(): Future[int] {....raises: [Exception, ValueError],
    +                                tags: [RootEffect], forbids: [].}
    +
    + + ok1 + +
    -
    -
    -
    proc asyncFun2(): owned(Future[void]) {....raises: [Exception], tags: [RootEffect].}
    -
    - - - -
    +
    +
    proc asyncFun2(): owned(Future[void]) {....raises: [Exception], tags: [RootEffect],
    +                                        forbids: [].}
    +
    + + + +
    -
    -
    -
    proc asyncFun3(): owned(Future[void]) {....raises: [Exception], tags: [RootEffect].}
    -
    - - +
    +
    proc asyncFun3(): owned(Future[void]) {....raises: [Exception], tags: [RootEffect],
    +                                        forbids: [].}
    +
    + +

    Example:

    discard
    ok1 - -
    + +
    -
    -
    -
    proc bar[T](a, b: T): T
    -
    - - - -
    +
    +
    proc bar[T](a, b: T): T
    +
    + + + +
    -
    -
    -
    proc baz() {....raises: [], tags: [].}
    -
    - - - -
    +
    +
    proc baz() {....raises: [], tags: [], forbids: [].}
    +
    + + + +
    -
    proc baz[T](a, b: T): T {....deprecated.}
    -
    -
    - Deprecated -
    - -This is deprecated without message. - -
    +
    proc baz[T](a, b: T): T {....deprecated.}
    +
    +
    + Deprecated
    + This is deprecated without message. + +
    +
    -
    -
    proc buzz[T](a, b: T): T {....deprecated: "since v0.20".}
    -
    -
    - Deprecated: since v0.20 -
    - -This is deprecated with a message. - -
    +
    +
    proc buzz[T](a, b: T): T {....deprecated: "since v0.20".}
    +
    +
    + Deprecated: since v0.20
    + This is deprecated with a message. + +
    +
    -
    -
    proc c_nonexistent(frmt: cstring): cint {.importc: "nonexistent",
    -    header: "<stdio.h>", varargs, discardable, ...raises: [], tags: [].}
    -
    - - - -
    +
    +
    proc c_nonexistent(frmt: cstring): cint {.importc: "nonexistent",
    +    header: "<stdio.h>", varargs, discardable, ...raises: [], tags: [], forbids: [].}
    +
    + + + +
    -
    -
    -
    proc c_printf(frmt: cstring): cint {.importc: "printf", header: "<stdio.h>",
    -                                     varargs, discardable, ...raises: [], tags: [].}
    -
    - -the c printf. etc. - -
    +
    +
    proc c_printf(frmt: cstring): cint {.importc: "printf", header: "<stdio.h>",
    +                                     varargs, discardable, ...raises: [], tags: [],
    +                                     forbids: [].}
    +
    + + the c printf. etc. + +
    -
    -
    -
    proc fromUtils3() {....raises: [], tags: [].}
    -
    - -came form utils but should be shown where fromUtilsGen is called +
    +
    proc fromUtils3() {....raises: [], tags: [], forbids: [].}
    +
    + + came form utils but should be shown where fromUtilsGen is called

    Example:

    discard """should be shown as examples for fromUtils3
            in module calling fromUtilsGen"""
    - -
    + +
    -
    -
    -
    proc isValid[T](x: T): bool
    -
    - - - -
    +
    +
    proc isValid[T](x: T): bool
    +
    + + + +
    -
    -
    -
    proc low[T: Ordinal | enum | range](x: T): T {.magic: "Low", noSideEffect,
    -    ...raises: [], tags: [].}
    -
    - -

    Returns the lowest possible value of an ordinal value x. As a special semantic rule, x may also be a type identifier.

    +
    +
    proc low[T: Ordinal | enum | range](x: T): T {.magic: "Low", noSideEffect,
    +    ...raises: [], tags: [], forbids: [].}
    +
    + +

    Returns the lowest possible value of an ordinal value x. As a special semantic rule, x may also be a type identifier.

    See also:

    low(2) # => -9223372036854775808
    - -
    + +
    -
    -
    -
    proc low2[T: Ordinal | enum | range](x: T): T {.magic: "Low", noSideEffect,
    -    ...raises: [], tags: [].}
    -
    - -

    Returns the lowest possible value of an ordinal value x. As a special semantic rule, x may also be a type identifier.

    +
    +
    proc low2[T: Ordinal | enum | range](x: T): T {.magic: "Low", noSideEffect,
    +    ...raises: [], tags: [], forbids: [].}
    +
    + +

    Returns the lowest possible value of an ordinal value x. As a special semantic rule, x may also be a type identifier.

    See also:

    low2(2) # => -9223372036854775808

    Example:

    discard "in low2"
    - -
    + +
    -
    -
    -
    proc p1() {....raises: [], tags: [].}
    -
    - -cp1 +
    +
    proc p1() {....raises: [], tags: [], forbids: [].}
    +
    + + cp1

    Example:

    doAssert 1 == 1 # regular comments work here
    c4

    Example:

    @@ -753,30 +695,28 @@

    Procs

    ]## discard "c9" # also work after
    - - + +
    -
    -
    -
    func someFunc() {....raises: [], tags: [].}
    -
    - -My someFunc. Stuff in quotes here. Some link - -
    +
    +
    func someFunc() {....raises: [], tags: [], forbids: [].}
    +
    + + My someFunc. Stuff in quotes here. Some link + +
    -
    -
    -
    proc tripleStrLitTest() {....raises: [], tags: [].}
    -
    - - +
    +
    proc tripleStrLitTest() {....raises: [], tags: [], forbids: [].}
    +
    + +

    Example: cmd: --hint:XDeclaredButNotUsed:off

    ## mullitline string litterals are tricky as their indentation can span
     ## below that of the runnableExamples
    @@ -813,365 +753,343 @@ 

    Procs

    """ ] discard # should be in
    - -
    + +
    -
    -
    -
    proc z1(): Foo {....raises: [], tags: [].}
    -
    - -cz1 - -
    +
    +
    proc z1(): Foo {....raises: [], tags: [], forbids: [].}
    +
    + + cz1 + +
    -
    -
    -
    proc z2() {....raises: [], tags: [].}
    -
    - -cz2 +
    +
    proc z2() {....raises: [], tags: [], forbids: [].}
    +
    + + cz2

    Example:

    discard "in cz2"
    - -
    + +
    -
    -
    -
    proc z3() {....raises: [], tags: [].}
    -
    - -cz3 - -
    +
    +
    proc z3() {....raises: [], tags: [], forbids: [].}
    +
    + + cz3 + +
    -
    -
    -
    proc z4() {....raises: [], tags: [].}
    -
    - -cz4 - -
    +
    +
    proc z4() {....raises: [], tags: [], forbids: [].}
    +
    + + cz4 + +
    -
    -
    -
    proc z5(): int {....raises: [], tags: [].}
    -
    - -cz5 - -
    +
    +
    proc z5(): int {....raises: [], tags: [], forbids: [].}
    +
    + + cz5 + +
    -
    -
    -
    proc z6(): int {....raises: [], tags: [].}
    -
    - -cz6 - -
    +
    +
    proc z6(): int {....raises: [], tags: [], forbids: [].}
    +
    + + cz6 + +
    -
    -
    -
    proc z7(): int {....raises: [], tags: [].}
    -
    - -cz7 - -
    +
    +
    proc z7(): int {....raises: [], tags: [], forbids: [].}
    +
    + + cz7 + +
    -
    -
    -
    proc z8(): int {....raises: [], tags: [].}
    -
    - -cz8 - -
    +
    +
    proc z8(): int {....raises: [], tags: [], forbids: [].}
    +
    + + cz8 + +
    -
    -
    -
    proc z9() {....raises: [], tags: [].}
    -
    - - +
    +
    proc z9() {....raises: [], tags: [], forbids: [].}
    +
    + +

    Example:

    doAssert 1 + 1 == 2
    - -
    + +
    -
    -
    -
    proc z10() {....raises: [], tags: [].}
    -
    - - +
    +
    proc z10() {....raises: [], tags: [], forbids: [].}
    +
    + +

    Example: cmd: -d:foobar

    discard 1
    cz10 - -
    + +
    -
    -
    -
    proc z11() {....raises: [], tags: [].}
    -
    - - +
    +
    proc z11() {....raises: [], tags: [], forbids: [].}
    +
    + +

    Example:

    discard 1
    - -
    + +
    -
    -
    -
    proc z12(): int {....raises: [], tags: [].}
    -
    - - +
    +
    proc z12(): int {....raises: [], tags: [], forbids: [].}
    +
    + +

    Example:

    discard 1
    - -
    + +
    -
    -
    -
    proc z13() {....raises: [], tags: [].}
    -
    - -cz13 +
    +
    proc z13() {....raises: [], tags: [], forbids: [].}
    +
    + + cz13

    Example:

    discard
    - -
    + +
    -
    -
    -
    proc z17() {....raises: [], tags: [].}
    -
    - -cz17 rest +
    +
    proc z17() {....raises: [], tags: [], forbids: [].}
    +
    + + cz17 rest

    Example:

    discard 1
    rest - -
    + +
    -
    + +
    -

    Methods

    -
    - -
    -
    -
    method method1(self: Moo) {.base, ...raises: [], tags: [].}
    -
    - -foo1 - -
    +

    Methods

    +
    +
    +
    +
    method method1(self: Moo) {.base, ...raises: [], tags: [], forbids: [].}
    +
    + + foo1 + +
    -
    -
    -
    method method2(self: Moo): int {.base, ...raises: [], tags: [].}
    -
    - -foo2 - -
    +
    +
    method method2(self: Moo): int {.base, ...raises: [], tags: [], forbids: [].}
    +
    + + foo2 + +
    -
    -
    -
    method method3(self: Moo): int {.base, ...raises: [], tags: [].}
    -
    - -foo3 - -
    +
    +
    method method3(self: Moo): int {.base, ...raises: [], tags: [], forbids: [].}
    +
    + + foo3 + +
    -
    +
    +
    -

    Iterators

    -
    - -
    -
    -
    iterator fromUtils1(): int {....raises: [], tags: [].}
    -
    - - +

    Iterators

    +
    +
    +
    +
    iterator fromUtils1(): int {....raises: [], tags: [], forbids: [].}
    +
    + +

    Example:

    # ok1
     assert 1 == 1
     # ok2
    - -
    + +
    -
    -
    -
    iterator iter1(n: int): int {....raises: [], tags: [].}
    -
    - -foo1 - -
    +
    +
    iterator iter1(n: int): int {....raises: [], tags: [], forbids: [].}
    +
    + + foo1 + +
    -
    -
    -
    iterator iter2(n: int): int {....raises: [], tags: [].}
    -
    - -foo2 +
    +
    iterator iter2(n: int): int {....raises: [], tags: [], forbids: [].}
    +
    + + foo2

    Example:

    discard # bar
    - -
    + +
    -
    + +
    -

    Macros

    -
    - -
    -
    -
    macro bar(): untyped
    -
    - - - -
    +

    Macros

    +
    +
    +
    +
    macro bar(): untyped
    +
    + + + +
    -
    -
    -
    macro z16()
    -
    - - +
    +
    macro z16()
    +
    + +

    Example:

    discard 1
    cz16 after

    Example:

    doAssert 2 == 1 + 1
    - -
    + +
    -
    -
    -
    macro z18(): int
    -
    - -cz18 - -
    +
    +
    macro z18(): int
    +
    + + cz18 + +
    -
    +
    +
    -

    Templates

    -
    - -
    -
    -
    template foo(a, b: SomeType)
    -
    - -This does nothing - -
    +

    Templates

    +
    +
    +
    +
    template foo(a, b: SomeType)
    +
    + + This does nothing + +
    -
    -
    -
    template fromUtils2()
    -
    - -ok3 +
    +
    template fromUtils2()
    +
    + + ok3

    Example:

    discard """should be shown as examples for fromUtils2
            in module calling fromUtilsGen"""
    - -
    + +
    -
    -
    -
    template myfn()
    -
    - - +
    +
    template myfn()
    +
    + +

    Example:

    import std/strutils
     ## issue #8871 preserve formatting
    @@ -1187,58 +1105,54 @@ 

    Templates

    block: discard 0xff # elu par cette crapule # should be in
    should be still in - -
    + +
    -
    -
    -
    template testNimDocTrailingExample()
    -
    - - +
    +
    template testNimDocTrailingExample()
    +
    + +

    Example:

    discard 2
    - -
    + +
    -
    -
    -
    template z6t(): int
    -
    - -cz6t - -
    +
    +
    template z6t(): int
    +
    + + cz6t + +
    -
    -
    -
    template z14()
    -
    - -cz14 +
    +
    template z14()
    +
    + + cz14

    Example:

    discard
    - -
    + +
    -
    -
    -
    template z15()
    -
    - -cz15 +
    +
    template z15()
    +
    + + cz15

    Example:

    discard

    Example:

    @@ -1249,26 +1163,25 @@

    Templates

    assert true

    Example:

    discard 1
    in or out? - -
    + +
    -
    +
    +
    -
    -
    - + diff --git a/nimdoc/testproject/expected/theindex.html b/nimdoc/testproject/expected/theindex.html index 47fae249150e..c62b4c7db7fb 100644 --- a/nimdoc/testproject/expected/theindex.html +++ b/nimdoc/testproject/expected/theindex.html @@ -1,12 +1,11 @@ - + - + - +Index @@ -17,17 +16,16 @@ -Index + - -
    -
    -

    Index

    - Modules: subdir/subdir_b/utils, testproject.

    API symbols

    +
    +
    +

    Index

    + Modules: subdir/subdir_b/utils, testproject.

    API symbols

    `$`:
    -
    -
    - + diff --git a/nimdoc/testproject/subdir/subdir_b/utils.nim b/nimdoc/testproject/subdir/subdir_b/utils.nim index e201a3d3884a..f535d7f74390 100644 --- a/nimdoc/testproject/subdir/subdir_b/utils.nim +++ b/nimdoc/testproject/subdir/subdir_b/utils.nim @@ -79,7 +79,9 @@ func fn9*(a: int): int = 42 ## comment func fn10*(a: int): int = a ## comment # Note capital letter N will be handled correctly in -# group references like fN11_ or fn11_: +# group references like fN11_ or fn11_ +# (or [fN11] or [fn11] in Markdown Syntax): + func fN11*() = discard func fN11*(x: int) = discard @@ -160,3 +162,52 @@ proc fn*[T; U, V: SomeFloat]() = discard ## Ref. `'big`_ or `func \`'big\``_ or `\`'big\`(string)`_. func `'big`*(a: string): SomeType = discard + +##[ + +Pandoc Markdown +=============== + +Now repeat all the auto links of above in Pandoc Markdown Syntax. + +Ref group [fn2] or specific function like [fn2()] +or [fn2( int )] or [fn2(int, +float)]. + +Ref generics like this: [binarySearch] or [binarySearch(openArray[T], K, +proc (T, K))] or [proc binarySearch(openArray[T], K, proc (T, K))] or +in different style: [proc binarysearch(openarray[T], K, proc(T, K))]. +Can be combined with export symbols and type parameters: +[binarysearch*[T, K](openArray[T], K, proc (T, K))]. +With spaces [binary search]. + +Note that `proc` can be used in postfix form: [binarySearch proc]. + +Ref. type like [G] and [type G] and [G[T]] and [type G*[T]]. + +Group ref. with capital letters works: [fN11] or [fn11] + +Ref. [`[]`] is the same as [proc `[]`(G[T])] because there are no +overloads. The full form: [proc `[]`*[T](x: G[T]): T] +Ref. [`[]=`] aka [`[]=`(G[T], int, T)]. +Ref. [$] aka [proc $] or [proc `$`]. +Ref. [$(a: ref SomeType)]. +Ref. [foo_bar] aka [iterator foo_bar_]. +Ref. [fn[T; U,V: SomeFloat]()]. +Ref. ['big] or [func `'big`] or [`'big`(string)]. + +Link name syntax +---------------- + +Pandoc Markdown has synax for changing text of links: +Ref. [this proc][`[]`] or [another symbol][G[T]]. + +Symbols documentation +--------------------- + +Let us repeat auto links from symbols section below: + +There is also variant [f(G[string])]. +See also [f(G[int])]. + +]## diff --git a/nimsuggest/nimsuggest.nim b/nimsuggest/nimsuggest.nim index c139b8b1726c..4fee8b2a2e35 100644 --- a/nimsuggest/nimsuggest.nim +++ b/nimsuggest/nimsuggest.nim @@ -7,6 +7,12 @@ # distribution, for details about the copyright. # +import compiler/renderer +import strformat +import tables +import std/sha1 +import times + ## Nimsuggest is a tool that helps to give editors IDE like capabilities. when not defined(nimcore): @@ -22,6 +28,7 @@ import compiler / [options, commands, modules, sem, idents, modulegraphs, prefixmatches, lineinfos, cmdlinehelper, pathutils] + when defined(windows): import winlean else: @@ -43,6 +50,8 @@ Options: --debug enable debug output --log enable verbose logging to nimsuggest.log file --v1 use version 1 of the protocol; for backwards compatibility + --v2 use version 2(default) of the protocol + --v3 use version 3 of the protocol --refresh perform automatic refreshes to keep the analysis precise --maxresults:N limit the number of suggestions to N --tester implies --stdin and outputs a line @@ -76,6 +85,9 @@ var requests: Channel[string] results: Channel[Suggest] +proc executeNoHooksV3(cmd: IdeCmd, file: AbsoluteFile, dirtyfile: AbsoluteFile, line, col: int; + graph: ModuleGraph); + proc writelnToChannel(line: string) = results.send(Suggest(section: ideMsg, doc: line)) @@ -137,7 +149,7 @@ proc listEpc(): SexpNode = argspecs = sexp("file line column dirtyfile".split(" ").map(newSSymbol)) docstring = sexp("line starts at 1, column at 0, dirtyfile is optional") result = newSList() - for command in ["sug", "con", "def", "use", "dus", "chk", "mod"]: + for command in ["sug", "con", "def", "use", "dus", "chk", "mod", "globalSymbols", "recompile", "saved", "chkFile"]: let cmd = sexp(command) methodDesc = newSList() @@ -163,6 +175,11 @@ proc symFromInfo(graph: ModuleGraph; trackPos: TLineInfo): PSym = proc executeNoHooks(cmd: IdeCmd, file, dirtyfile: AbsoluteFile, line, col: int; graph: ModuleGraph) = let conf = graph.config + + if conf.suggestVersion == 3: + executeNoHooksV3(cmd, file, dirtyfile, line, col, graph) + return + myLog("cmd: " & $cmd & ", file: " & file.string & ", dirtyFile: " & dirtyfile.string & "[" & $line & ":" & $col & "]") @@ -436,6 +453,11 @@ proc execCmd(cmd: string; graph: ModuleGraph; cachedMsgs: CachedMsgs) = of "terse": toggle optIdeTerse of "known": conf.ideCmd = ideKnown of "project": conf.ideCmd = ideProject + of "changed": conf.ideCmd = ideChanged + of "globalsymbols": conf.ideCmd = ideGlobalSymbols + of "chkfile": conf.ideCmd = ideChkFile + of "recompile": conf.ideCmd = ideRecompile + of "type": conf.ideCmd = ideType else: err() var dirtyfile = "" var orig = "" @@ -463,14 +485,23 @@ proc execCmd(cmd: string; graph: ModuleGraph; cachedMsgs: CachedMsgs) = execute(conf.ideCmd, AbsoluteFile orig, AbsoluteFile dirtyfile, line, col, graph) sentinel() +template benchmark(benchmarkName: string, code: untyped) = + block: + myLog "Started [" & benchmarkName & "]..." + let t0 = epochTime() + code + let elapsed = epochTime() - t0 + let elapsedStr = elapsed.formatFloat(format = ffDecimal, precision = 3) + myLog "CPU Time [" & benchmarkName & "] " & elapsedStr & "s" + proc recompileFullProject(graph: ModuleGraph) = - #echo "recompiling full project" - resetSystemArtifacts(graph) - graph.vm = nil - graph.resetAllModules() - GC_fullCollect() - compileProject(graph) - #echo GC_getStatistics() + benchmark "Recompilation(clean)": + graph.resetForBackend() + graph.resetSystemArtifacts() + graph.vm = nil + graph.resetAllModules() + GC_fullCollect() + graph.compileProject() proc mainThread(graph: ModuleGraph) = let conf = graph.config @@ -499,7 +530,7 @@ proc mainThread(graph: ModuleGraph) = else: os.sleep 250 idle += 1 - if idle == 20 and gRefresh: + if idle == 20 and gRefresh and conf.suggestVersion != 3: # we use some nimsuggest activity to enable a lazy recompile: conf.ideCmd = ideChk conf.writelnHook = proc (s: string) = discard @@ -527,12 +558,18 @@ proc mainCommand(graph: ModuleGraph) = conf.setErrorMaxHighMaybe # honor --errorMax even if it may not make sense here # do not print errors, but log them - conf.writelnHook = myLog - conf.structuredErrorHook = nil + conf.writelnHook = proc (msg: string) = discard + + if graph.config.suggestVersion == 3: + graph.config.structuredErrorHook = proc (conf: ConfigRef; info: TLineInfo; msg: string; sev: Severity) = + let suggest = Suggest(section: ideChk, filePath: toFullPath(conf, info), + line: toLinenumber(info), column: toColumn(info), doc: msg, forth: $sev) + graph.suggestErrors.mgetOrPut(info.fileIndex, @[]).add suggest # compile the project before showing any input so that we already # can answer questions right away: - compileProject(graph) + benchmark "Initial compilation": + compileProject(graph) open(requests) open(results) @@ -584,8 +621,9 @@ proc processCmdLine*(pass: TCmdLinePass, cmd: string; conf: ConfigRef) = gMode = mepc conf.verbosity = 0 # Port number gotta be first. of "debug": incl(conf.globalOptions, optIdeDebug) - of "v2": conf.suggestVersion = 0 of "v1": conf.suggestVersion = 1 + of "v2": conf.suggestVersion = 0 + of "v3": conf.suggestVersion = 3 of "tester": gMode = mstdin gEmitEof = true @@ -647,6 +685,187 @@ proc handleCmdLine(cache: IdentCache; conf: ConfigRef) = if self.loadConfigsAndProcessCmdLine(cache, conf, graph): mainCommand(graph) +# v3 start + +proc recompilePartially(graph: ModuleGraph, projectFileIdx = InvalidFileIdx) = + if projectFileIdx == InvalidFileIdx: + myLog "Recompiling partially from root" + else: + myLog fmt "Recompiling partially starting from {graph.getModule(projectFileIdx)}" + + # inst caches are breaking incremental compilation when the cache caches stuff + # from dirty buffer + # TODO: investigate more efficient way to achieve the same + # graph.typeInstCache.clear() + # graph.procInstCache.clear() + + GC_fullCollect() + + try: + benchmark "Recompilation": + graph.compileProject(projectFileIdx) + except Exception as e: + myLog fmt "Failed to recompile partially with the following error:\n {e.msg} \n\n {e.getStackTrace()}" + try: + graph.recompileFullProject() + except Exception as e: + myLog fmt "Failed clean recompilation:\n {e.msg} \n\n {e.getStackTrace()}" + +proc findSymData(graph: ModuleGraph, file: AbsoluteFile; line, col: int): + ref SymInfoPair = + let + fileIdx = fileInfoIdx(graph.config, file) + trackPos = newLineInfo(fileIdx, line, col) + for s in graph.fileSymbols(fileIdx): + if isTracked(s.info, trackPos, s.sym.name.s.len): + new(result) + result[] = s + break + +proc markDirtyIfNeeded(graph: ModuleGraph, file: string, originalFileIdx: FileIndex) = + let sha = $sha1.secureHashFile(file) + if graph.config.m.fileInfos[originalFileIdx.int32].hash != sha or graph.config.ideCmd == ideSug: + myLog fmt "{file} changed compared to last compilation" + graph.markDirty originalFileIdx + graph.markClientsDirty originalFileIdx + else: + myLog fmt "No changes in file {file} compared to last compilation" + +proc suggestResult(graph: ModuleGraph, sym: PSym, info: TLineInfo, defaultSection = ideNone) = + let section = if defaultSection != ideNone: + defaultSection + elif sym.info.exactEquals(info): + ideDef + else: + ideUse + let suggest = symToSuggest(graph, sym, isLocal=false, section, + info, 100, PrefixMatch.None, false, 0) + suggestResult(graph.config, suggest) + +const + # kinds for ideOutline and ideGlobalSymbols + searchableSymKinds = {skField, skEnumField, skIterator, skMethod, skFunc, skProc, skConverter, skTemplate} + +proc executeNoHooksV3(cmd: IdeCmd, file: AbsoluteFile, dirtyfile: AbsoluteFile, line, col: int; + graph: ModuleGraph) = + let conf = graph.config + conf.writelnHook = proc (s: string) = discard + conf.structuredErrorHook = proc (conf: ConfigRef; info: TLineInfo; + msg: string; sev: Severity) = + let suggest = Suggest(section: ideChk, filePath: toFullPath(conf, info), + line: toLinenumber(info), column: toColumn(info), doc: msg, forth: $sev) + graph.suggestErrors.mgetOrPut(info.fileIndex, @[]).add suggest + + conf.ideCmd = cmd + + myLog fmt "cmd: {cmd}, file: {file}[{line}:{col}], dirtyFile: {dirtyfile}" + + var fileIndex: FileIndex + + if not (cmd in {ideRecompile, ideGlobalSymbols}): + if not fileInfoKnown(conf, file): + myLog fmt "{file} is unknown, returning no results" + return + + fileIndex = fileInfoIdx(conf, file) + msgs.setDirtyFile( + conf, + fileIndex, + if dirtyfile.isEmpty: AbsoluteFile"" else: dirtyfile) + + if not dirtyfile.isEmpty: + graph.markDirtyIfNeeded(dirtyFile.string, fileInfoIdx(conf, file)) + + # these commands require fully compiled project + if cmd in {ideUse, ideDus, ideGlobalSymbols, ideChk} and graph.needsCompilation(): + graph.recompilePartially() + # when doing incremental build for the project root we should make sure that + # everything is unmarked as no longer beeing dirty in case there is no + # longer reference to a particular module. E. g. A depends on B, B is marked + # as dirty and A loses B import. + graph.unmarkAllDirty() + + # these commands require partially compiled project + elif cmd in {ideSug, ideOutline, ideHighlight, ideDef, ideChkFile, ideType} and + (graph.needsCompilation(fileIndex) or cmd == ideSug): + # for ideSug use v2 implementation + if cmd == ideSug: + conf.m.trackPos = newLineInfo(fileIndex, line, col) + conf.m.trackPosAttached = false + else: + conf.m.trackPos = default(TLineInfo) + + graph.recompilePartially(fileIndex) + + case cmd + of ideDef: + let s = graph.findSymData(file, line, col) + if not s.isNil: + graph.suggestResult(s.sym, s.sym.info) + of ideType: + let s = graph.findSymData(file, line, col) + if not s.isNil: + let typeSym = s.sym.typ.sym + if typeSym != nil: + graph.suggestResult(typeSym, typeSym.info, ideType) + elif s.sym.typ.len != 0: + let genericType = s.sym.typ[0].sym + graph.suggestResult(genericType, genericType.info, ideType) + of ideUse, ideDus: + let symbol = graph.findSymData(file, line, col) + if not symbol.isNil: + for s in graph.suggestSymbolsIter: + if s.sym == symbol.sym: + graph.suggestResult(s.sym, s.info) + of ideHighlight: + let sym = graph.findSymData(file, line, col) + if not sym.isNil: + let usages = graph.fileSymbols(fileIndex).filterIt(it.sym == sym.sym) + myLog fmt "Found {usages.len} usages in {file.string}" + for s in usages: + graph.suggestResult(s.sym, s.info) + of ideRecompile: + graph.recompileFullProject() + of ideChanged: + graph.markDirtyIfNeeded(file.string, fileIndex) + of ideSug: + # ideSug performs partial build of the file, thus mark it dirty for the + # future calls. + graph.markDirtyIfNeeded(file.string, fileIndex) + of ideOutline: + let + module = graph.getModule fileIndex + symbols = graph.fileSymbols(fileIndex) + .filterIt(it.sym.info.exactEquals(it.info) and + (it.sym.owner == module or + it.sym.kind in searchableSymKinds)) + + for s in symbols: + graph.suggestResult(s.sym, s.info, ideOutline) + of ideChk: + myLog fmt "Reporting errors for {graph.suggestErrors.len} file(s)" + for sug in graph.suggestErrorsIter: + suggestResult(graph.config, sug) + of ideChkFile: + let errors = graph.suggestErrors.getOrDefault(fileIndex, @[]) + myLog fmt "Reporting {errors.len} error(s) for {file.string}" + for error in errors: + suggestResult(graph.config, error) + of ideGlobalSymbols: + var counter = 0 + for s in graph.suggestSymbolsIter: + if (sfGlobal in s.sym.flags or s.sym.kind in searchableSymKinds) and + s.sym.info == s.info: + if contains(s.sym.name.s, file.string): + inc counter + graph.suggestResult(s.sym, s.info) + # stop after first 100 results + if counter > 100: + break + else: + myLog fmt "Discarding {cmd}" + +# v3 end when isMainModule: handleCmdLine(newIdentCache(), newConfigRef()) else: @@ -726,8 +945,9 @@ else: if self.loadConfigsAndProcessCmdLine(cache, conf, graph): mockCommand(graph) if gLogging: + log("Search paths:") for it in conf.searchPaths: - log(it.string) + log(" " & it.string) retval.doStopCompile = proc (): bool = false return NimSuggest(graph: retval, idle: 0, cachedMsgs: @[]) diff --git a/nimsuggest/sexp.nim b/nimsuggest/sexp.nim index 31f4988e1274..467f922cc99b 100644 --- a/nimsuggest/sexp.nim +++ b/nimsuggest/sexp.nim @@ -288,10 +288,6 @@ proc newSString*(s: string): SexpNode = ## Creates a new `SString SexpNode`. result = SexpNode(kind: SString, str: s) -proc newSStringMove(s: string): SexpNode = - result = SexpNode(kind: SString) - shallowCopy(result.str, s) - proc newSInt*(n: BiggestInt): SexpNode = ## Creates a new `SInt SexpNode`. result = SexpNode(kind: SInt, num: n) @@ -315,10 +311,6 @@ proc newSList*(): SexpNode = proc newSSymbol*(s: string): SexpNode = result = SexpNode(kind: SSymbol, symbol: s) -proc newSSymbolMove(s: string): SexpNode = - result = SexpNode(kind: SSymbol) - shallowCopy(result.symbol, s) - proc getStr*(n: SexpNode, default: string = ""): string = ## Retrieves the string value of a `SString SexpNode`. ## @@ -596,8 +588,7 @@ proc parseSexp(p: var SexpParser): SexpNode = case p.tok of tkString: # we capture 'p.a' here, so we need to give it a fresh buffer afterwards: - result = newSStringMove(p.a) - p.a = "" + result = SexpNode(kind: SString, str: move p.a) discard getTok(p) of tkInt: result = newSInt(parseBiggestInt(p.a)) @@ -609,8 +600,7 @@ proc parseSexp(p: var SexpParser): SexpNode = result = newSNil() discard getTok(p) of tkSymbol: - result = newSSymbolMove(p.a) - p.a = "" + result = SexpNode(kind: SSymbol, symbol: move p.a) discard getTok(p) of tkParensLe: result = newSList() diff --git a/nimsuggest/tester.nim b/nimsuggest/tester.nim index 1db33706ab7a..6e068e0675c3 100644 --- a/nimsuggest/tester.nim +++ b/nimsuggest/tester.nim @@ -252,7 +252,10 @@ proc runEpcTest(filename: string): int = for cmd in s.startup: if not runCmd(cmd, s.dest): quit "invalid command: " & cmd - let epccmd = s.cmd.replace("--tester", "--epc --v2 --log") + let epccmd = if s.cmd.contains("--v3"): + s.cmd.replace("--tester", "--epc --log") + else: + s.cmd.replace("--tester", "--epc --v2 --log") let cl = parseCmdLine(epccmd) var p = startProcess(command=cl[0], args=cl[1 .. ^1], options={poStdErrToStdOut, poUsePath, @@ -279,7 +282,7 @@ proc runEpcTest(filename: string): int = socket.sendEpcStr(req) let sx = parseSexp(socket.recvEpc()) if not req.startsWith("mod "): - let answer = sexpToAnswer(sx) + let answer = if sx[2].kind == SNil: "" else: sexpToAnswer(sx) doReport(filename, answer, resp, report) socket.sendEpcStr "return arg" diff --git a/nimsuggest/tests/tsug_typedecl.nim b/nimsuggest/tests/tsug_typedecl.nim index 833043d31d50..2a510929db0d 100644 --- a/nimsuggest/tests/tsug_typedecl.nim +++ b/nimsuggest/tests/tsug_typedecl.nim @@ -21,6 +21,6 @@ $nimsuggest --tester $file >sug $1 sug;;skType;;tsug_typedecl.someType;;someType;;*nimsuggest/tests/tsug_typedecl.nim;;7;;2;;"";;100;;Prefix sug;;skType;;tsug_typedecl.super;;super;;*nimsuggest/tests/tsug_typedecl.nim;;6;;2;;"";;100;;Prefix -sug;;skType;;system.string;;string;;*lib/system.nim;;*;;*;;*;;100;;Prefix +sug;;skType;;system.string;;string;;*lib/system/basic_types.nim;;*;;*;;*;;100;;Prefix sug;;skType;;system.seq;;seq;;*lib/system.nim;;*;;*;;*;;100;;Prefix -""" \ No newline at end of file +""" diff --git a/nimsuggest/tests/ttype_decl.nim b/nimsuggest/tests/ttype_decl.nim index 6022392d00a2..61d8c26cd0a2 100644 --- a/nimsuggest/tests/ttype_decl.nim +++ b/nimsuggest/tests/ttype_decl.nim @@ -2,8 +2,8 @@ discard """ $nimsuggest --tester --maxresults:3 $file >sug $1 sug;;skType;;ttype_decl.Other;;Other;;$file;;10;;2;;"";;100;;None -sug;;skType;;system.int;;int;;*/lib/system/basic_types.nim;;2;;2;;"";;100;;None -sug;;skType;;system.string;;string;;*/lib/system.nim;;34;;2;;"";;100;;None +sug;;skType;;system.int;;int;;*lib/system/basic_types.nim;;2;;2;;"";;100;;None +sug;;skType;;system.string;;string;;*lib/system/basic_types.nim;;23;;2;;"";;100;;None """ import strutils type diff --git a/nimsuggest/tests/tuse_structure.nim b/nimsuggest/tests/tuse_structure.nim new file mode 100644 index 000000000000..f65ab9060e98 --- /dev/null +++ b/nimsuggest/tests/tuse_structure.nim @@ -0,0 +1,15 @@ +# tests for use and structures + +type + Foo* = ref object of RootObj + bar*: string + +proc test(f: Foo) = + echo f.#[!]#bar + +discard """ +$nimsuggest --tester $file +>use $1 +def skField tuse_structure.Foo.bar string $file 5 4 "" 100 +use skField tuse_structure.Foo.bar string $file 8 9 "" 100 +""" diff --git a/nimsuggest/tests/tv3.nim b/nimsuggest/tests/tv3.nim new file mode 100644 index 000000000000..57ad86e4d1b3 --- /dev/null +++ b/nimsuggest/tests/tv3.nim @@ -0,0 +1,31 @@ +# tests v3 + +type + Foo* = ref object of RootObj + bar*: string + +proc test(f: Foo) = + echo f.ba#[!]#r + +#[!]# + +discard """ +$nimsuggest --v3 --tester $file +>use $1 +def skField tv3.Foo.bar string $file 5 4 "" 100 +use skField tv3.Foo.bar string $file 8 9 "" 100 +>def $1 +def skField tv3.Foo.bar string $file 5 4 "" 100 +>outline $1 +outline skType tv3.Foo Foo $file 4 2 "" 100 +outline skField tv3.Foo.bar string $file 5 4 "" 100 +outline skProc tv3.test proc (f: Foo){.gcsafe, locks: 0.} $file 7 5 "" 100 +>sug $1 +sug skField bar string $file 5 4 "" 100 Prefix +>globalSymbols test +def skProc tv3.test proc (f: Foo){.gcsafe, locks: 0.} $file 7 5 "" 100 +>globalSymbols Foo +def skType tv3.Foo Foo $file 4 2 "" 100 +>def $2 +>use $2 +""" diff --git a/nimsuggest/tests/tv3_definition.nim b/nimsuggest/tests/tv3_definition.nim new file mode 100644 index 000000000000..03684b7cd51c --- /dev/null +++ b/nimsuggest/tests/tv3_definition.nim @@ -0,0 +1,9 @@ + +let foo = 30 +let bar = foo + fo#[!]#o + foo + +discard """ +$nimsuggest --v3 --tester $file +>def $1 +def skLet tv3_definition.foo int $file 2 4 "" 100 +""" diff --git a/nimsuggest/tests/tv3_import.nim b/nimsuggest/tests/tv3_import.nim new file mode 100644 index 000000000000..3c128f85b08c --- /dev/null +++ b/nimsuggest/tests/tv3_import.nim @@ -0,0 +1,7 @@ +import tv#[!]#3 + +discard """ +$nimsuggest --v3 --tester $file +>def $1 +def skModule tv3 */tv3.nim 1 0 "" 100 +""" diff --git a/nimsuggest/tests/tv3_typeDefinition.nim b/nimsuggest/tests/tv3_typeDefinition.nim new file mode 100644 index 000000000000..f86d12cc67bf --- /dev/null +++ b/nimsuggest/tests/tv3_typeDefinition.nim @@ -0,0 +1,32 @@ +# tests v3 + +type + Foo* = ref object of RootObj + bar*: string + +proc test(ff: Foo) = + echo f#[!]#f.bar + +type + Fo#[!]#o2* = ref object of RootObj + +type + FooGeneric[T] = ref object of RootObj + bar*: T + +let fooGeneric = FooGeneric[string]() +echo fo#[!]#oGeneric.bar + +# bad type +echo unde#[!]#fined + +discard """ +$nimsuggest --v3 --tester $file +>type $1 +type skType tv3_typeDefinition.Foo Foo $file 4 2 "" 100 +>type $2 +type skType tv3_typeDefinition.Foo2 Foo2 $file 11 2 "" 100 +>type $3 +type skType tv3_typeDefinition.FooGeneric FooGeneric $file 14 2 "" 100 +>type $4 +""" diff --git a/readme.md b/readme.md index 7ff056a20dad..3d32bcf87120 100644 --- a/readme.md +++ b/readme.md @@ -51,8 +51,8 @@ Nim programming language. Those C sources are available within the Next, to build from source you will need: - * A C compiler such as ``gcc`` 3.x/later or an alternative such as ``clang``, - ``Visual C++`` or ``Intel C++``. It is recommended to use ``gcc`` 3.x or + * A C compiler such as ``gcc`` 5.x/later or an alternative such as ``clang``, + ``Visual C++`` or ``Intel C++``. It is recommended to use ``gcc`` 5.x or later. * Either ``git`` or ``wget`` to download the needed source repositories. * The ``build-essential`` package when using ``gcc`` on Ubuntu (and likely @@ -88,9 +88,9 @@ Next, run the appropriate build shell script for your platform: Finally, once you have finished the build steps (on Windows, Mac, or Linux) you should add the ``bin`` directory to your PATH. -See also [rebuilding the compiler](doc/intern.rst#rebuilding-the-compiler). +See also [bootstrapping the compiler](https://nim-lang.github.io/Nim/intern.html#bootstrapping-the-compiler). -See also [reproducible builds](doc/intern.rst#reproducible-builds). +See also [reproducible builds](https://nim-lang.github.io/Nim/intern.html#bootstrapping-the-compiler-reproducible-builds). ## Koch @@ -104,7 +104,7 @@ can run a subset of tests by specifying a category (for example ``./koch tests cat async``). For more information on the ``koch`` build tool please see the documentation -within the [doc/koch.rst](doc/koch.rst) file. +within the [doc/koch.md](https://nim-lang.github.io/Nim/koch.html) file. ## Nimble @@ -141,7 +141,7 @@ you should familiarize yourself with the following repository structure: dependencies written in other languages. * ``wrappers/`` - modules that wrap dependencies written in other languages. * ``tests/`` - contains categorized tests for the compiler and standard library. -* ``tools/`` - the tools including ``niminst`` and ``nimweb`` (mostly invoked via +* ``tools/`` - the tools including ``niminst`` (mostly invoked via ``koch``). * ``koch.nim`` - the tool used to bootstrap Nim, generate C sources, build the website, and generate the documentation. diff --git a/testament/categories.nim b/testament/categories.nim index 46cc3b210908..e8b13746a1bc 100644 --- a/testament/categories.nim +++ b/testament/categories.nim @@ -13,7 +13,7 @@ # included from testament.nim import important_packages -import std/strformat +import std/[strformat, strutils] from std/sequtils import filterIt const @@ -59,10 +59,10 @@ proc runBasicDLLTest(c, r: var TResults, cat: Category, options: string) = var test2 = makeTest("tests/dll/server.nim", options & " --threads:on" & rpath, cat) test2.spec.action = actionCompile testSpec c, test2 - var test3 = makeTest("lib/nimhcr.nim", options & " --outdir:tests/dll" & rpath, cat) + var test3 = makeTest("lib/nimhcr.nim", options & " --threads:off --outdir:tests/dll" & rpath, cat) test3.spec.action = actionCompile testSpec c, test3 - var test4 = makeTest("tests/dll/visibility.nim", options & " --app:lib" & rpath, cat) + var test4 = makeTest("tests/dll/visibility.nim", options & " --threads:off --app:lib" & rpath, cat) test4.spec.action = actionCompile testSpec c, test4 @@ -77,13 +77,13 @@ proc runBasicDLLTest(c, r: var TResults, cat: Category, options: string) = defer: putEnv(libpathenv, libpath) testSpec r, makeTest("tests/dll/client.nim", options & " --threads:on" & rpath, cat) - testSpec r, makeTest("tests/dll/nimhcr_unit.nim", options & rpath, cat) - testSpec r, makeTest("tests/dll/visibility.nim", options & rpath, cat) + testSpec r, makeTest("tests/dll/nimhcr_unit.nim", options & " --threads:off" & rpath, cat) + testSpec r, makeTest("tests/dll/visibility.nim", options & " --threads:off" & rpath, cat) if "boehm" notin options: # force build required - see the comments in the .nim file for more details var hcri = makeTest("tests/dll/nimhcr_integration.nim", - options & " --forceBuild --hotCodeReloading:on" & rpath, cat) + options & " --threads:off --forceBuild --hotCodeReloading:on" & rpath, cat) let nimcache = nimcacheDir(hcri.name, hcri.options, getTestSpecTarget()) let cmd = prepareTestCmd(hcri.spec.getCmd, hcri.name, hcri.options, nimcache, getTestSpecTarget()) @@ -94,8 +94,8 @@ proc dllTests(r: var TResults, cat: Category, options: string) = # dummy compile result: var c = initResults() - runBasicDLLTest c, r, cat, options - runBasicDLLTest c, r, cat, options & " -d:release" + runBasicDLLTest c, r, cat, options & " --mm:refc" + runBasicDLLTest c, r, cat, options & " -d:release --mm:refc" when not defined(windows): # still cannot find a recent Windows version of boehm.dll: runBasicDLLTest c, r, cat, options & " --gc:boehm" @@ -105,9 +105,9 @@ proc dllTests(r: var TResults, cat: Category, options: string) = proc gcTests(r: var TResults, cat: Category, options: string) = template testWithoutMs(filename: untyped) = - testSpec r, makeTest("tests/gc" / filename, options, cat) + testSpec r, makeTest("tests/gc" / filename, options & "--mm:refc", cat) testSpec r, makeTest("tests/gc" / filename, options & - " -d:release -d:useRealtimeGC", cat) + " -d:release -d:useRealtimeGC --mm:refc", cat) when filename != "gctest": testSpec r, makeTest("tests/gc" / filename, options & " --gc:orc", cat) @@ -437,7 +437,7 @@ proc testNimblePackages(r: var TResults; cat: Category; packageFilter: string) = if pkg.allowFailure: inc r.passed inc r.failedButAllowed - addResult(r, test, targetC, "", cmd & "\n" & outp, reFailed, allowFailure = pkg.allowFailure) + addResult(r, test, targetC, "", "", cmd & "\n" & outp, reFailed, allowFailure = pkg.allowFailure) continue outp @@ -448,23 +448,26 @@ proc testNimblePackages(r: var TResults; cat: Category; packageFilter: string) = let describeOutput = tryCommand("git describe --tags --abbrev=0") discard tryCommand("git checkout $#" % [describeOutput.strip.quoteShell]) discard tryCommand("nimble install --depsOnly -y", maxRetries = 3) - discard tryCommand(pkg.cmd, reFailed = reBuildFailed) + let cmds = pkg.cmd.split(';') + for i in 0 ..< cmds.len - 1: + discard tryCommand(cmds[i], maxRetries = 3) + discard tryCommand(cmds[^1], reFailed = reBuildFailed) inc r.passed - r.addResult(test, targetC, "", "", reSuccess, allowFailure = pkg.allowFailure) + r.addResult(test, targetC, "", "", "", reSuccess, allowFailure = pkg.allowFailure) errors = r.total - r.passed if errors == 0: - r.addResult(packageFileTest, targetC, "", "", reSuccess) + r.addResult(packageFileTest, targetC, "", "", "", reSuccess) else: - r.addResult(packageFileTest, targetC, "", "", reBuildFailed) + r.addResult(packageFileTest, targetC, "", "", "", reBuildFailed) except JsonParsingError: errors = 1 - r.addResult(packageFileTest, targetC, "", "Invalid package file", reBuildFailed) + r.addResult(packageFileTest, targetC, "", "", "Invalid package file", reBuildFailed) raise except ValueError: errors = 1 - r.addResult(packageFileTest, targetC, "", "Unknown package", reBuildFailed) + r.addResult(packageFileTest, targetC, "", "", "Unknown package", reBuildFailed) raise # bug #18805 finally: if errors == 0: removeDir(packagesDir) diff --git a/testament/important_packages.nim b/testament/important_packages.nim index 6b0b15fd8853..392587e7b5c8 100644 --- a/testament/important_packages.nim +++ b/testament/important_packages.nim @@ -37,9 +37,9 @@ pkg "alea", allowFailure = true pkg "argparse" pkg "arraymancer", "nim c tests/tests_cpu.nim" pkg "ast_pattern_matching", "nim c -r --oldgensym:on tests/test1.nim", allowFailure = true -pkg "asyncthreadpool" +pkg "asyncthreadpool", "nimble test --mm:refc" pkg "awk" -pkg "bigints", url = "https://github.com/Araq/nim-bigints" +pkg "bigints" pkg "binaryheap", "nim c -r binaryheap.nim" pkg "BipBuffer" pkg "blscurve", allowFailure = true # pending https://github.com/status-im/nim-blscurve/issues/39 @@ -48,7 +48,7 @@ pkg "brainfuck", "nim c -d:release -r tests/compile.nim" pkg "bump", "nim c --gc:arc --path:. -r tests/tbump.nim", "https://github.com/disruptek/bump" pkg "c2nim", "nim c testsuite/tester.nim" pkg "cascade" -pkg "cello" +pkg "cello", url = "https://github.com/nim-lang/cello", useHead = true pkg "chroma" pkg "chronicles", "nim c -o:chr -r chronicles.nim" pkg "chronos", "nim c -r -d:release tests/testall" @@ -90,26 +90,26 @@ pkg "markdown" pkg "memo" pkg "msgpack4nim", "nim c -r tests/test_spec.nim" pkg "nake", "nim c nakefile.nim" -pkg "neo", "nim c -d:blas=openblas tests/all.nim" -pkg "nesm", "nimble tests" # notice plural 'tests' +pkg "neo", "nim c -d:blas=openblas --mm:refc tests/all.nim" +pkg "nesm", "nimble tests", "https://github.com/nim-lang/NESM", useHead = true pkg "netty" pkg "nico", allowFailure = true pkg "nicy", "nim c -r src/nicy.nim" pkg "nigui", "nim c -o:niguii -r src/nigui.nim" pkg "nimcrypto", "nim r --path:. tests/testall.nim" # `--path:.` workaround needed, see D20210308T165435 pkg "NimData", "nim c -o:nimdataa src/nimdata.nim" -pkg "nimes", "nim c src/nimes.nim" +pkg "nimes", "nimble install -y sdl2@#HEAD;nim c src/nimes.nim" pkg "nimfp", "nim c -o:nfp -r src/fp.nim" -pkg "nimgame2", "nim c nimgame2/nimgame.nim" +pkg "nimgame2", "nim c --mm:refc nimgame2/nimgame.nim" # XXX Doesn't work with deprecated 'randomize', will create a PR. pkg "nimgen", "nim c -o:nimgenn -r src/nimgen/runcfg.nim" -pkg "nimlsp", allowFailure = true +pkg "nimlsp", allowFailure = true # dependency on ast_pattern_matching pkg "nimly", "nim c -r tests/test_readme_example.nim" pkg "nimongo", "nimble test_ci", allowFailure = true pkg "nimph", "nimble test", "https://github.com/disruptek/nimph", allowFailure = true pkg "nimpy", "nim c -r tests/nimfrompy.nim" pkg "nimquery" -pkg "nimsl" +pkg "nimsl", "nimble install -y variant@#HEAD;nimble test", "https://github.com/nim-lang/nimsl", useHead = true pkg "nimsvg" pkg "nimterop", "nimble minitest" pkg "nimwc", "nim c nimwc.nim" @@ -125,7 +125,7 @@ pkg "patty" pkg "pixie" pkg "plotly", "nim c examples/all.nim" pkg "pnm" -pkg "polypbren" +pkg "polypbren", allowFailure = true pkg "prologue", "nimble tcompile" pkg "protobuf", "nim c -o:protobuff -r src/protobuf.nim" pkg "pylib" @@ -142,13 +142,13 @@ pkg "sim" pkg "snip", "nimble test", "https://github.com/genotrance/snip" pkg "stint", "nim r stint.nim" pkg "strslice" -pkg "strunicode", "nim c -r src/strunicode.nim" +pkg "strunicode", "nim c -r --mm:refc src/strunicode.nim" pkg "supersnappy" pkg "synthesis" pkg "telebot", "nim c -o:tbot -r src/telebot.nim" pkg "tempdir" pkg "templates" -pkg "tensordsl", "nim c -r tests/tests.nim", "https://krux02@bitbucket.org/krux02/tensordslnim.git" +pkg "tensordsl", "nim c -r --mm:refc tests/tests.nim", "https://krux02@bitbucket.org/krux02/tensordslnim.git" pkg "terminaltables", "nim c src/terminaltables.nim" pkg "termstyle", "nim c -r termstyle.nim" pkg "timeit" @@ -157,7 +157,7 @@ pkg "tiny_sqlite" pkg "unicodedb", "nim c -d:release -r tests/tests.nim" pkg "unicodeplus", "nim c -d:release -r tests/tests.nim" pkg "unpack" -pkg "weave", "nimble test_gc_arc" +pkg "weave", "nimble install -y cligen synthesis;nimble test_gc_arc" pkg "websocket", "nim c websocket.nim" pkg "winim", "nim c winim.nim" pkg "with" diff --git a/testament/lib/stdtest/specialpaths.nim b/testament/lib/stdtest/specialpaths.nim index 53b94fdbb0fa..7df63666f959 100644 --- a/testament/lib/stdtest/specialpaths.nim +++ b/testament/lib/stdtest/specialpaths.nim @@ -13,6 +13,8 @@ import compiler/nimpaths ]# import os +when defined(nimPreviewSlimSystem): + import std/assertions # Note: all the const paths defined here are known at compile time and valid # so long Nim repo isn't relocated after compilation. diff --git a/testament/lib/stdtest/unittest_light.nim b/testament/lib/stdtest/unittest_light.nim index 273bf72f57e6..4ab1d7543525 100644 --- a/testament/lib/stdtest/unittest_light.nim +++ b/testament/lib/stdtest/unittest_light.nim @@ -1,3 +1,6 @@ +import std/assertions + + proc mismatch*[T](lhs: T, rhs: T): string = ## Simplified version of `unittest.require` that satisfies a common use case, ## while avoiding pulling too many dependencies. On failure, diagnostic diff --git a/testament/specs.nim b/testament/specs.nim index 42400f22dacf..cbb73a5f2d82 100644 --- a/testament/specs.nim +++ b/testament/specs.nim @@ -75,6 +75,7 @@ type # xxx make sure `isJoinableSpec` takes into account each field here. action*: TTestAction file*, cmd*: string + filename*: string ## Test filename (without path). input*: string outputCheck*: TOutputCheck sortoutput*: bool @@ -127,19 +128,56 @@ when not declared(parseCfgBool): of "n", "no", "false", "0", "off": result = false else: raise newException(ValueError, "cannot interpret as a bool: " & s) +proc addLine*(self: var string; pieces: varargs[string]) = + for piece in pieces: + self.add piece + self.add "\n" + + const - inlineErrorMarker = "#[tt." + inlineErrorKindMarker = "tt." + inlineErrorMarker = "#[" & inlineErrorKindMarker proc extractErrorMsg(s: string; i: int; line: var int; col: var int; spec: var TSpec): int = + ## Extract inline error messages. + ## + ## Can parse a single message for a line: + ## + ## .. code-block:: nim + ## + ## proc generic_proc*[T] {.no_destroy, userPragma.} = #[tt.Error + ## ^ 'generic_proc' should be: 'genericProc' [Name] ]# + ## + ## Can parse multiple messages for a line when they are separated by ';': + ## + ## .. code-block:: nim + ## + ## proc generic_proc*[T] {.no_destroy, userPragma.} = #[tt.Error + ## ^ 'generic_proc' should be: 'genericProc' [Name]; tt.Error + ## ^ 'no_destroy' should be: 'nodestroy' [Name]; tt.Error + ## ^ 'userPragma' should be: 'user_pragma' [template declared in mstyleCheck.nim(10, 9)] [Name] ]# + ## + ## .. code-block:: nim + ## + ## proc generic_proc*[T] {.no_destroy, userPragma.} = #[tt.Error + ## ^ 'generic_proc' should be: 'genericProc' [Name]; + ## tt.Error ^ 'no_destroy' should be: 'nodestroy' [Name]; + ## tt.Error ^ 'userPragma' should be: 'user_pragma' [template declared in mstyleCheck.nim(10, 9)] [Name] ]# + ## result = i + len(inlineErrorMarker) inc col, len(inlineErrorMarker) + let msgLine = line + var msgCol = -1 + var msg = "" var kind = "" - while result < s.len and s[result] in IdentChars: - kind.add s[result] - inc result - inc col - var caret = (line, -1) + template parseKind = + while result < s.len and s[result] in IdentChars: + kind.add s[result] + inc result + inc col + if kind notin ["Hint", "Warning", "Error"]: + spec.parseErrors.addLine "expected inline message kind: Hint, Warning, Error" template skipWhitespace = while result < s.len and s[result] in Whitespace: @@ -150,34 +188,70 @@ proc extractErrorMsg(s: string; i: int; line: var int; col: var int; spec: var T inc col inc result + template parseCaret = + if result < s.len and s[result] == '^': + msgCol = col + inc result + inc col + skipWhitespace() + else: + spec.parseErrors.addLine "expected column marker ('^') for inline message" + + template isMsgDelimiter: bool = + s[result] == ';' and + (block: + let nextTokenIdx = result + 1 + parseutils.skipWhitespace(s, result + 1) + if s.len > nextTokenIdx + len(inlineErrorKindMarker) and + s[nextTokenIdx..(nextTokenIdx + len(inlineErrorKindMarker) - 1)] == inlineErrorKindMarker: + true + else: + false) + + template trimTrailingMsgWhitespace = + while msg.len > 0 and msg[^1] in Whitespace: + setLen msg, msg.len - 1 + + template addInlineError = + doAssert msg[^1] notin Whitespace + if kind == "Error": spec.action = actionReject + spec.inlineErrors.add InlineError(kind: kind, msg: msg, line: msgLine, col: msgCol) + + parseKind() skipWhitespace() - if result < s.len and s[result] == '^': - caret = (line-1, col) - inc result - inc col - skipWhitespace() + parseCaret() - var msg = "" while result < s.len-1: if s[result] == '\n': + msg.add '\n' inc result inc line col = 1 - elif s[result] == ']' and s[result+1] == '#': - while msg.len > 0 and msg[^1] in Whitespace: - setLen msg, msg.len - 1 - + elif isMsgDelimiter(): + trimTrailingMsgWhitespace() inc result + skipWhitespace() + addInlineError() + inc result, len(inlineErrorKindMarker) + inc col, 1 + len(inlineErrorKindMarker) + kind.setLen 0 + msg.setLen 0 + parseKind() + skipWhitespace() + parseCaret() + elif s[result] == ']' and s[result+1] == '#': + trimTrailingMsgWhitespace() + inc result, 2 inc col, 2 - if kind == "Error": spec.action = actionReject - spec.unjoinable = true - spec.inlineErrors.add InlineError(kind: kind, msg: msg, line: caret[0], col: caret[1]) + addInlineError() break else: msg.add s[result] inc result inc col + if spec.inlineErrors.len > 0: + spec.unjoinable = true + proc extractSpec(filename: string; spec: var TSpec): string = const tripleQuote = "\"\"\"" @@ -229,15 +303,6 @@ proc parseTargets*(value: string): set[TTarget] = of "js": result.incl(targetJS) else: raise newException(ValueError, "invalid target: '$#'" % v) -proc addLine*(self: var string; a: string) = - self.add a - self.add "\n" - -proc addLine*(self: var string; a, b: string) = - self.add a - self.add b - self.add "\n" - proc initSpec*(filename: string): TSpec = result.file = filename @@ -249,6 +314,7 @@ proc isCurrentBatch*(testamentData: TestamentData; filename: string): bool = proc parseSpec*(filename: string): TSpec = result.file = filename + result.filename = extractFilename(filename) let specStr = extractSpec(filename, result) var ss = newStringStream(specStr) var p: CfgParser @@ -439,3 +505,15 @@ proc parseSpec*(filename: string): TSpec = result.inCurrentBatch = isCurrentBatch(testamentData0, filename) or result.unbatchable if not result.inCurrentBatch: result.err = reDisabled + + # Interpolate variables in msgs: + template varSub(msg: string): string = + try: + msg % ["/", $DirSep, "file", result.filename] + except ValueError: + result.parseErrors.addLine "invalid variable interpolation (see 'https://nim-lang.github.io/Nim/testament.html#writing-unitests-output-message-variable-interpolation')" + msg + result.nimout = result.nimout.varSub + result.msg = result.msg.varSub + for inlineError in result.inlineErrors.mitems: + inlineError.msg = inlineError.msg.varSub diff --git a/testament/testament.nim b/testament/testament.nim index 4b5a8a147930..a63194ebaa57 100644 --- a/testament/testament.nim +++ b/testament/testament.nim @@ -54,7 +54,7 @@ Options: --verbose print commands (compiling and running tests) --simulate see what tests would be run but don't run them (for debugging) --failing only show failing/ignored tests - --targets:"c cpp js objc" run tests for specified targets (default: all) + --targets:"c cpp js objc" run tests for specified targets (default: c) --nim:path use a particular nim executable (default: $$PATH/nim) --directory:dir Change to directory dir before reading the tests or doing anything else. --colors:on|off Turn messages coloring on|off. @@ -258,18 +258,23 @@ Tests failed and allowed to fail: $3 / $1
    Tests skipped: $4 / $1
    """ % [$x.total, $x.passed, $x.failedButAllowed, $x.skipped] -proc addResult(r: var TResults, test: TTest, target: TTarget, - expected, given: string, successOrig: TResultEnum, allowFailure = false, givenSpec: ptr TSpec = nil) = - # instead of `ptr TSpec` we could also use `Option[TSpec]`; passing `givenSpec` makes it easier to get what we need - # instead of having to pass individual fields, or abusing existing ones like expected vs given. - # test.name is easier to find than test.name.extractFilename - # A bit hacky but simple and works with tests/testament/tshould_not_work.nim +proc testName(test: TTest, target: TTarget, extraOptions: string, allowFailure: bool): string = var name = test.name.replace(DirSep, '/') name.add ' ' & $target if allowFailure: name.add " (allowed to fail) " if test.options.len > 0: name.add ' ' & test.options + if extraOptions.len > 0: name.add ' ' & extraOptions + name.strip() +proc addResult(r: var TResults, test: TTest, target: TTarget, + extraOptions, expected, given: string, successOrig: TResultEnum, + allowFailure = false, givenSpec: ptr TSpec = nil) = + # instead of `ptr TSpec` we could also use `Option[TSpec]`; passing `givenSpec` makes it easier to get what we need + # instead of having to pass individual fields, or abusing existing ones like expected vs given. + # test.name is easier to find than test.name.extractFilename + # A bit hacky but simple and works with tests/testament/tshould_not_work.nim + let name = testName(test, target, extraOptions, allowFailure) let duration = epochTime() - test.startTime let success = if test.spec.timeout > 0.0 and duration > test.spec.timeout: reTimeout else: successOrig @@ -333,45 +338,24 @@ proc addResult(r: var TResults, test: TTest, target: TTarget, discard waitForExit(p) close(p) -proc checkForInlineErrors(r: var TResults, expected, given: TSpec, test: TTest, target: TTarget) = - let pegLine = peg"{[^(]*} '(' {\d+} ', ' {\d+} ') ' {[^:]*} ':' \s* {.*}" - var covered = initIntSet() - for line in splitLines(given.nimout): - - if line =~ pegLine: - let file = extractFilename(matches[0]) - let line = try: parseInt(matches[1]) except: -1 - let col = try: parseInt(matches[2]) except: -1 - let kind = matches[3] - let msg = matches[4] - - if file == extractFilename test.name: - var i = 0 - for x in expected.inlineErrors: - if x.line == line and (x.col == col or x.col < 0) and - x.kind == kind and x.msg in msg: - covered.incl i - inc i - - block coverCheck: - for j in 0..high(expected.inlineErrors): - if j notin covered: - var e = test.name - e.add '(' - e.addInt expected.inlineErrors[j].line - if expected.inlineErrors[j].col > 0: - e.add ", " - e.addInt expected.inlineErrors[j].col - e.add ") " - e.add expected.inlineErrors[j].kind - e.add ": " - e.add expected.inlineErrors[j].msg - - r.addResult(test, target, e, given.nimout, reMsgsDiffer) - break coverCheck - - r.addResult(test, target, "", given.msg, reSuccess) - inc(r.passed) +proc toString(inlineError: InlineError, filename: string): string = + result.add "$file($line, $col) $kind: $msg" % [ + "file", filename, + "line", $inlineError.line, + "col", $inlineError.col, + "kind", $inlineError.kind, + "msg", $inlineError.msg + ] + +proc inlineErrorsMsgs(expected: TSpec): string = + for inlineError in expected.inlineErrors.items: + result.addLine inlineError.toString(expected.filename) + +proc checkForInlineErrors(expected, given: TSpec): bool = + for inlineError in expected.inlineErrors: + if inlineError.toString(expected.filename) notin given.nimout: + return false + true proc nimoutCheck(expected, given: TSpec): bool = result = true @@ -381,22 +365,24 @@ proc nimoutCheck(expected, given: TSpec): bool = elif expected.nimout.len > 0 and not greedyOrderedSubsetLines(expected.nimout, given.nimout): result = false -proc cmpMsgs(r: var TResults, expected, given: TSpec, test: TTest, target: TTarget) = - if expected.inlineErrors.len > 0: - checkForInlineErrors(r, expected, given, test, target) +proc cmpMsgs(r: var TResults, expected, given: TSpec, test: TTest, + target: TTarget, extraOptions: string) = + if not checkForInlineErrors(expected, given) or + (not expected.nimoutFull and not nimoutCheck(expected, given)): + r.addResult(test, target, extraOptions, expected.nimout & inlineErrorsMsgs(expected), given.nimout, reMsgsDiffer) elif strip(expected.msg) notin strip(given.msg): - r.addResult(test, target, expected.msg, given.msg, reMsgsDiffer) + r.addResult(test, target, extraOptions, expected.msg, given.msg, reMsgsDiffer) elif not nimoutCheck(expected, given): - r.addResult(test, target, expected.nimout, given.nimout, reMsgsDiffer) + r.addResult(test, target, extraOptions, expected.nimout, given.nimout, reMsgsDiffer) elif extractFilename(expected.file) != extractFilename(given.file) and "internal error:" notin expected.msg: - r.addResult(test, target, expected.file, given.file, reFilesDiffer) + r.addResult(test, target, extraOptions, expected.filename, given.file, reFilesDiffer) elif expected.line != given.line and expected.line != 0 or expected.column != given.column and expected.column != 0: - r.addResult(test, target, $expected.line & ':' & $expected.column, + r.addResult(test, target, extraOptions, $expected.line & ':' & $expected.column, $given.line & ':' & $given.column, reLinesDiffer) else: - r.addResult(test, target, expected.msg, given.msg, reSuccess) + r.addResult(test, target, extraOptions, expected.msg, given.msg, reSuccess) inc(r.passed) proc generatedFile(test: TTest, target: TTarget): string = @@ -434,22 +420,23 @@ proc codegenCheck(test: TTest, target: TTarget, spec: TSpec, expectedMsg: var st given.err = reCodeNotFound echo getCurrentExceptionMsg() -proc compilerOutputTests(test: TTest, target: TTarget, given: var TSpec, - expected: TSpec; r: var TResults) = +proc compilerOutputTests(test: TTest, target: TTarget, extraOptions: string, + given: var TSpec, expected: TSpec; r: var TResults) = var expectedmsg: string = "" var givenmsg: string = "" if given.err == reSuccess: if expected.needsCodegenCheck: codegenCheck(test, target, expected, expectedmsg, given) givenmsg = given.msg - if not nimoutCheck(expected, given): + if not nimoutCheck(expected, given) or + not checkForInlineErrors(expected, given): given.err = reMsgsDiffer - expectedmsg = expected.nimout + expectedmsg = expected.nimout & inlineErrorsMsgs(expected) givenmsg = given.nimout.strip else: givenmsg = "$ " & given.cmd & '\n' & given.nimout if given.err == reSuccess: inc(r.passed) - r.addResult(test, target, expectedmsg, givenmsg, given.err) + r.addResult(test, target, extraOptions, expectedmsg, givenmsg, given.err) proc getTestSpecTarget(): TTarget = if getEnv("NIM_COMPILE_TO_CPP", "false") == "true": @@ -457,16 +444,6 @@ proc getTestSpecTarget(): TTarget = else: result = targetC -proc checkDisabled(r: var TResults, test: TTest): bool = - if test.spec.err in {reDisabled, reJoined}: - # targetC is a lie, but parameter is required - r.addResult(test, targetC, "", "", test.spec.err) - inc(r.skipped) - inc(r.total) - result = false - else: - result = true - var count = 0 proc equalModuloLastNewline(a, b: string): bool = @@ -474,30 +451,32 @@ proc equalModuloLastNewline(a, b: string): bool = result = a == b or b.endsWith("\n") and a == b[0 ..< ^1] proc testSpecHelper(r: var TResults, test: var TTest, expected: TSpec, - target: TTarget, nimcache: string, extraOptions = "") = + target: TTarget, extraOptions: string, nimcache: string) = test.startTime = epochTime() - template callNimCompilerImpl(): untyped = - # xxx this used to also pass: `--stdout --hint:Path:off`, but was done inconsistently - # with other branches - callNimCompiler(expected.getCmd, test.name, test.options, nimcache, target, extraOptions) + if testName(test, target, extraOptions, false) in skips: + test.spec.err = reDisabled + + if test.spec.err in {reDisabled, reJoined}: + r.addResult(test, target, extraOptions, "", "", test.spec.err) + inc(r.skipped) + return + var given = callNimCompiler(expected.getCmd, test.name, test.options, nimcache, target, extraOptions) case expected.action of actionCompile: - var given = callNimCompilerImpl() - compilerOutputTests(test, target, given, expected, r) + compilerOutputTests(test, target, extraOptions, given, expected, r) of actionRun: - var given = callNimCompilerImpl() if given.err != reSuccess: - r.addResult(test, target, "", "$ " & given.cmd & '\n' & given.nimout, given.err, givenSpec = given.addr) + r.addResult(test, target, extraOptions, "", "$ " & given.cmd & '\n' & given.nimout, given.err, givenSpec = given.addr) else: let isJsTarget = target == targetJS var exeFile = changeFileExt(test.name, if isJsTarget: "js" else: ExeExt) if not fileExists(exeFile): - r.addResult(test, target, expected.output, + r.addResult(test, target, extraOptions, expected.output, "executable not found: " & exeFile, reExeNotFound) else: let nodejs = if isJsTarget: findNodeJs() else: "" if isJsTarget and nodejs == "": - r.addResult(test, target, expected.output, "nodejs binary not in PATH", + r.addResult(test, target, extraOptions, expected.output, "nodejs binary not in PATH", reExeNotFound) else: var exeCmd: string @@ -528,24 +507,22 @@ proc testSpecHelper(r: var TResults, test: var TTest, expected: TSpec, else: buf if exitCode != expected.exitCode: - r.addResult(test, target, "exitcode: " & $expected.exitCode, + r.addResult(test, target, extraOptions, "exitcode: " & $expected.exitCode, "exitcode: " & $exitCode & "\n\nOutput:\n" & bufB, reExitcodesDiffer) elif (expected.outputCheck == ocEqual and not expected.output.equalModuloLastNewline(bufB)) or (expected.outputCheck == ocSubstr and expected.output notin bufB): given.err = reOutputsDiffer - r.addResult(test, target, expected.output, bufB, reOutputsDiffer) - else: - compilerOutputTests(test, target, given, expected, r) + r.addResult(test, target, extraOptions, expected.output, bufB, reOutputsDiffer) + compilerOutputTests(test, target, extraOptions, given, expected, r) of actionReject: - let given = callNimCompilerImpl() - cmpMsgs(r, expected, given, test, target) + cmpMsgs(r, expected, given, test, target, extraOptions) -proc targetHelper(r: var TResults, test: TTest, expected: TSpec, extraOptions = "") = +proc targetHelper(r: var TResults, test: TTest, expected: TSpec, extraOptions: string) = for target in expected.targets: inc(r.total) if target notin gTargets: - r.addResult(test, target, "", "", reDisabled) + r.addResult(test, target, extraOptions, "", "", reDisabled) inc(r.skipped) elif simulate: inc count @@ -553,16 +530,15 @@ proc targetHelper(r: var TResults, test: TTest, expected: TSpec, extraOptions = else: let nimcache = nimcacheDir(test.name, test.options, target) var testClone = test - testSpecHelper(r, testClone, expected, target, nimcache, extraOptions) + testSpecHelper(r, testClone, expected, target, extraOptions, nimcache) proc testSpec(r: var TResults, test: TTest, targets: set[TTarget] = {}) = var expected = test.spec if expected.parseErrors.len > 0: # targetC is a lie, but a parameter is required - r.addResult(test, targetC, "", expected.parseErrors, reInvalidSpec) + r.addResult(test, targetC, "", "", expected.parseErrors, reInvalidSpec) inc(r.total) return - if not checkDisabled(r, test): return expected.targets.incl targets # still no target specified at all @@ -572,14 +548,13 @@ proc testSpec(r: var TResults, test: TTest, targets: set[TTarget] = {}) = for m in test.spec.matrix: targetHelper(r, test, expected, m) else: - targetHelper(r, test, expected) + targetHelper(r, test, expected, "") proc testSpecWithNimcache(r: var TResults, test: TTest; nimcache: string) {.used.} = - if not checkDisabled(r, test): return for target in test.spec.targets: inc(r.total) var testClone = test - testSpecHelper(r, testClone, test.spec, target, nimcache) + testSpecHelper(r, testClone, test.spec, target, "", nimcache) proc makeTest(test, options: string, cat: Category): TTest = result.cat = cat @@ -599,7 +574,6 @@ proc makeRawTest(test, options: string, cat: Category): TTest {.used.} = # TODO: fix these files const disabledFilesDefault = @[ - "LockFreeHash.nim", "tableimpl.nim", "setimpl.nim", "hashcommon.nim", diff --git a/tests/arc/t16558.nim b/tests/arc/t16558.nim index 7b6eb46692bb..0dbe02b3319c 100644 --- a/tests/arc/t16558.nim +++ b/tests/arc/t16558.nim @@ -1,6 +1,6 @@ discard """ matrix: "--gc:arc" - errormsg: "expression cannot be cast to int" + errormsg: "expression cannot be cast to 'int'" """ block: # bug #16558 diff --git a/tests/arc/t19401.nim b/tests/arc/t19401.nim new file mode 100644 index 000000000000..56702a4a2770 --- /dev/null +++ b/tests/arc/t19401.nim @@ -0,0 +1,32 @@ +discard """ + output: ''' +delete foo +delete foo +delete foo +''' + matrix: "--mm:arc" +""" + +type Foo = ref object + data: int +proc delete(self: Foo) +proc newFoo: Foo = + let x = 12 + discard x + new(result, delete) + result.data = x +proc delete(self: Foo) = + doAssert self.data == 12 + echo("delete foo") + +if isMainModule: + proc test() = + let x1 = newFoo() + let x2 = newFoo() + discard x1 + discard x2 + var x3: Foo + new(x3, delete) + x3.data = 12 + discard x3 + test() diff --git a/tests/arc/t19402.nim b/tests/arc/t19402.nim new file mode 100644 index 000000000000..5ee6fc798620 --- /dev/null +++ b/tests/arc/t19402.nim @@ -0,0 +1,32 @@ +discard """ + output: ''' +delete foo +delete foo +delete foo +''' + matrix: "--mm:arc" +""" + +type Foo = ref object of RootObj + data: int +proc delete(self: Foo) +proc newFoo: Foo = + let x = 12 + discard x + new(result, delete) + result.data = x +proc delete(self: Foo) = + doAssert self.data == 12 + echo("delete foo") + +if isMainModule: + proc test() = + let x1 = newFoo() + let x2 = newFoo() + discard x1 + discard x2 + var x3: Foo + new(x3, delete) + x3.data = 12 + discard x3 + test() \ No newline at end of file diff --git a/tests/arc/t19457.nim b/tests/arc/t19457.nim new file mode 100644 index 000000000000..78447ce82a51 --- /dev/null +++ b/tests/arc/t19457.nim @@ -0,0 +1,16 @@ +discard """ + matrix: "--gc:refc; --gc:arc" +""" + +# bug #19457 +proc gcd(x, y: seq[int]): seq[int] = + var + a = x + b = y + while b[0] > 0: + let c = @[a[0] mod b[0]] + a = b + b = c + return a + +doAssert gcd(@[1], @[2]) == @[1] \ No newline at end of file diff --git a/tests/arc/t19862.nim b/tests/arc/t19862.nim new file mode 100644 index 000000000000..f7146ec2689d --- /dev/null +++ b/tests/arc/t19862.nim @@ -0,0 +1,13 @@ +discard """ + matrix: "--gc:refc; --gc:arc" +""" + +# bug #19862 +type NewString = object + +proc len(s: NewString): int = 10 + +converter toNewString(x: WideCStringObj): NewString = discard + +let w = newWideCString("test") +doAssert len(w) == 4 diff --git a/tests/arc/tarc_orc.nim b/tests/arc/tarc_orc.nim index 879ece3c7c64..c6a7845b0869 100644 --- a/tests/arc/tarc_orc.nim +++ b/tests/arc/tarc_orc.nim @@ -1,4 +1,5 @@ discard """ + targets: "c cpp" matrix: "--mm:arc; --mm:orc" """ @@ -20,3 +21,15 @@ block: let keys = initKeyPair() doAssert keys.public[0] == 88 + + +template minIndexByIt: untyped = + var other = 3 + other + +proc bug20303() = + var hlibs = @["hello", "world", "how", "are", "you"] + let res = hlibs[minIndexByIt()] + doAssert res == "are" + +bug20303() diff --git a/tests/arc/tcaseobj.nim b/tests/arc/tcaseobj.nim index a967a509bc9b..d52833e4d33d 100644 --- a/tests/arc/tcaseobj.nim +++ b/tests/arc/tcaseobj.nim @@ -10,6 +10,7 @@ begin end prevented (ok: true, value: "ok") +@[(kind: P, pChildren: @[])] myobj destroyed ''' """ @@ -244,3 +245,27 @@ block: doAssert j1.x1 == 2 doAssert j0.x0 == 2 + +# ------------------------------------ +# bug #20305 + +type + ContentNodeKind = enum + P, Br, Text + ContentNode = object + case kind: ContentNodeKind + of P: pChildren: seq[ContentNode] + of Br: discard + of Text: textStr: string + +proc bug20305 = + var x = ContentNode(kind: P, pChildren: @[ + ContentNode(kind: P, pChildren: @[ContentNode(kind: Text, textStr: "brrr")]) + ]) + x.pChildren.add ContentNode(kind: Br) + x.pChildren.del(0) + {.cast(uncheckedAssign).}: + x.pChildren[0].kind = P + echo x.pChildren + +bug20305() diff --git a/tests/arc/tcursorloop.nim b/tests/arc/tcursorloop.nim new file mode 100644 index 000000000000..a37a6a0366ef --- /dev/null +++ b/tests/arc/tcursorloop.nim @@ -0,0 +1,45 @@ +discard """ + cmd: '''nim c --gc:arc --expandArc:traverse --hint:Performance:off $file''' + nimout: ''' +--expandArc: traverse + +var + it + jt_cursor +try: + `=copy`(it, root) + block :tmp: + while ( + not (it == nil)): + if true: + echo [it.s] + `=copy`(it, it.ri) + jt_cursor = root + if ( + not (jt_cursor == nil)): + echo [jt_cursor.s] + jt_cursor = jt_cursor.ri +finally: + `=destroy`(it) +-- end of expandArc ------------------------ +''' +""" + +type + Node = ref object + le, ri: Node + s: string + +proc traverse(root: Node) = + var it = root + while it != nil: + if true: + echo it.s + it = it.ri + + var jt = root + if jt != nil: + echo jt.s + jt = jt.ri + +traverse(nil) diff --git a/tests/arc/topt_cursor.nim b/tests/arc/topt_cursor.nim index 5c35b454fbf0..794132921912 100644 --- a/tests/arc/topt_cursor.nim +++ b/tests/arc/topt_cursor.nim @@ -12,7 +12,7 @@ try: x_cursor = ("different", 54) else: x_cursor = ("string here", 80) echo [ - :tmpD = `$`(x_cursor) + :tmpD = `$$`(x_cursor) :tmpD] finally: `=destroy`(:tmpD) diff --git a/tests/arc/topt_no_cursor.nim b/tests/arc/topt_no_cursor.nim index 824d6bd08840..0de7f41b9ff4 100644 --- a/tests/arc/topt_no_cursor.nim +++ b/tests/arc/topt_no_cursor.nim @@ -1,5 +1,5 @@ discard """ - output: '''(repo: "", package: "meo", ext: "") + output: '''(package: "", ext: "meo") doing shady stuff... 3 6 @@ -11,24 +11,17 @@ doing shady stuff... cmd: '''nim c --gc:arc --expandArc:newTarget --expandArc:delete --expandArc:p1 --expandArc:tt --hint:Performance:off --assertions:off --expandArc:extractConfig --expandArc:mergeShadowScope --expandArc:check $file''' nimout: '''--expandArc: newTarget -var - splat - :tmp - :tmp_1 - :tmp_2 -splat = splitFile(path) -:tmp = splat.dir -wasMoved(splat.dir) -:tmp_1 = splat.name -wasMoved(splat.name) -:tmp_2 = splat.ext -wasMoved(splat.ext) +splat = splitDrive do: + let blitTmp = path + blitTmp +:tmp = splat.drive +wasMoved(splat.drive) +:tmp_1 = splat.path_1 +wasMoved(splat.path_1) result = ( - let blitTmp = :tmp - blitTmp, - let blitTmp_1 = :tmp_1 + let blitTmp_1 = :tmp blitTmp_1, - let blitTmp_2 = :tmp_2 + let blitTmp_2 = :tmp_1 blitTmp_2) `=destroy`(splat) -- end of expandArc ------------------------ @@ -78,7 +71,7 @@ try: `=copy`(:tmpD_1, it_cursor.val) :tmpD_1) echo [ - :tmpD_2 = `$`(a) + :tmpD_2 = `$$`(a) :tmpD_2] finally: `=destroy`(:tmpD_2) @@ -145,11 +138,11 @@ if dirExists(this.value): par = (dir_1: parentDir(this.value), front_1: wasMoved(:tmpD_1) `=copy`(:tmpD_1, - :tmpD_3 = splitPath do: + :tmpD_3 = splitDrive do: wasMoved(:tmpD_2) `=copy`(:tmpD_2, this.value) :tmpD_2 - :tmpD_3.tail) + :tmpD_3.path) :tmpD_1) `=destroy`(:tmpD_3) if dirExists(par.dir): @@ -160,13 +153,13 @@ else: -- end of expandArc ------------------------''' """ -import os +import os, std/private/ntpath -type Target = tuple[repo, package, ext: string] +type Target = tuple[package, ext: string] proc newTarget*(path: string): Target = - let splat = path.splitFile - result = (repo: splat.dir, package: splat.name, ext: splat.ext) + let splat = path.splitDrive + result = (package: splat.drive, ext: splat.path) echo newTarget("meo") @@ -364,7 +357,7 @@ proc getSubDirs(parent, front: string): seq[string] = @[] method check(this: Foo) {.base.} = this.isValid = fileExists(this.value) let par = if dirExists(this.value): (dir: this.value, front: "") - else: (dir: parentDir(this.value), front: splitPath(this.value).tail) + else: (dir: parentDir(this.value), front: splitDrive(this.value).path) if dirExists(par.dir): this.matchDirs = getSubDirs(par.dir, par.front) else: diff --git a/tests/arc/topt_refcursors.nim b/tests/arc/topt_refcursors.nim index f097150a34a1..c13d81badc1e 100644 --- a/tests/arc/topt_refcursors.nim +++ b/tests/arc/topt_refcursors.nim @@ -5,21 +5,28 @@ discard """ var it_cursor - jt_cursor -it_cursor = root -block :tmp: - while ( - not (it_cursor == nil)): - echo [it_cursor.s] - it_cursor = it_cursor.ri -jt_cursor = root -block :tmp_1: - while ( - not (jt_cursor == nil)): - var ri_1_cursor - ri_1_cursor = jt_cursor.ri - echo [jt_cursor.s] - jt_cursor = ri_1_cursor + jt +try: + it_cursor = root + block :tmp: + while ( + not (it_cursor == nil)): + echo [it_cursor.s] + it_cursor = it_cursor.ri + `=copy`(jt, root) + block :tmp_1: + while ( + not (jt == nil)): + var ri_1 + try: + `=copy`(ri_1, jt.ri) + echo [jt.s] + `=sink`(jt, ri_1) + wasMoved(ri_1) + finally: + `=destroy`(ri_1) +finally: + `=destroy`(jt) -- end of expandArc ------------------------''' """ diff --git a/tests/arc/tref_cast_error.nim b/tests/arc/tref_cast_error.nim index b0d2faf77158..20139c1be30b 100644 --- a/tests/arc/tref_cast_error.nim +++ b/tests/arc/tref_cast_error.nim @@ -1,6 +1,6 @@ discard """ cmd: "nim c --gc:arc $file" - errormsg: "expression cannot be cast to ref RootObj" + errormsg: "expression cannot be cast to 'ref RootObj'" joinable: false """ diff --git a/tests/assign/moverload_asgn2.nim b/tests/assign/moverload_asgn2.nim index 6620adbeb9dd..cfea48cd1c2e 100644 --- a/tests/assign/moverload_asgn2.nim +++ b/tests/assign/moverload_asgn2.nim @@ -1,3 +1,7 @@ +discard """ + matrix: "--mm:refc" +""" + type Concrete* = object a*, b*: string diff --git a/tests/assign/tassign.nim b/tests/assign/tassign.nim index da097ca83f03..fdec04d226dd 100644 --- a/tests/assign/tassign.nim +++ b/tests/assign/tassign.nim @@ -80,7 +80,7 @@ block tcopy: block tgenericassign: type - TAny = object {.pure.} + TAny {.pure.} = object value: pointer rawType: pointer diff --git a/tests/ast_pattern_matching.nim b/tests/ast_pattern_matching.nim index d209493b6c09..0ba0aa57a8ed 100644 --- a/tests/ast_pattern_matching.nim +++ b/tests/ast_pattern_matching.nim @@ -561,7 +561,7 @@ when isMainModule: echo "got the ident m" testRecCase: - type Obj[T] = object {.inheritable.} + type Obj[T] {.inheritable.} = object name: string case isFat: bool of true: diff --git a/tests/async/tasync_traceback.nim b/tests/async/tasync_traceback.nim index cd16b2257f57..ec67d34f9dad 100644 --- a/tests/async/tasync_traceback.nim +++ b/tests/async/tasync_traceback.nim @@ -1,6 +1,5 @@ discard """ exitcode: 0 - disabled: "windows" output: "Matched" """ import asyncdispatch, strutils @@ -122,28 +121,31 @@ Exception message: bar failure let resLines = splitLines(result.strip) let expLines = splitLines(expected.strip) -if resLines.len != expLines.len: - echo("Not matched! Wrong number of lines!") - echo expLines.len - echo resLines.len - echo("Expected: -----------") - echo expected - echo("Gotten: -------------") - echo result - echo("---------------------") - quit(QuitFailure) - -var ok = true -for i in 0 ..< resLines.len: - if not resLines[i].match(re(expLines[i])): - echo "Not matched! Line ", i + 1 - echo "Expected:" - echo expLines[i] - echo "Actual:" - echo resLines[i] - ok = false - -if ok: - echo("Matched") +when not defined(cpp): # todo fixme + if resLines.len != expLines.len: + echo("Not matched! Wrong number of lines!") + echo expLines.len + echo resLines.len + echo("Expected: -----------") + echo expected + echo("Gotten: -------------") + echo result + echo("---------------------") + quit(QuitFailure) + + var ok = true + for i in 0 ..< resLines.len: + if not resLines[i].match(re(expLines[i])): + echo "Not matched! Line ", i + 1 + echo "Expected:" + echo expLines[i] + echo "Actual:" + echo resLines[i] + ok = false + + if ok: + echo("Matched") + else: + quit(QuitFailure) else: - quit(QuitFailure) + echo("Matched") diff --git a/tests/avr/thello.nim b/tests/avr/thello.nim index a0191815cfc6..7ebaeae5fd09 100644 --- a/tests/avr/thello.nim +++ b/tests/avr/thello.nim @@ -1,5 +1,5 @@ discard """ - cmd: "nim c --compileOnly --os:standalone --exceptions:quirky -d:noSignalHandler -d:danger $file" + cmd: "nim c --compileOnly --os:standalone --exceptions:quirky -d:noSignalHandler -d:danger --threads:off $file" action: "compile" """ diff --git a/tests/bind/tbind2.nim b/tests/bind/told_bind_expr.nim similarity index 74% rename from tests/bind/tbind2.nim rename to tests/bind/told_bind_expr.nim index fc2eeda1a06a..56389eaf63b3 100644 --- a/tests/bind/tbind2.nim +++ b/tests/bind/told_bind_expr.nim @@ -1,9 +1,10 @@ discard """ errormsg: "ambiguous call" - file: "tbind2.nim" - line: 12 + file: "told_bind_expr.nim" + line: 13 """ -# Test the new ``bind`` keyword for templates + +# Pre-0.9 deprecated bind expression syntax proc p1(x: int8, y: int): int = return x + y proc p1(x: int, y: int8): int = return x - y diff --git a/tests/casestmt/tcasestmt.nim b/tests/casestmt/tcasestmt.nim index 53cccdb64183..3a4907494ad5 100644 --- a/tests/casestmt/tcasestmt.nim +++ b/tests/casestmt/tcasestmt.nim @@ -287,3 +287,14 @@ doAssert(foo2("Y", "a2") == 0) doAssert(foo2("Y", "2a") == 2) doAssert(foo2("N", "a3") == 3) doAssert(foo2("z", "2") == 0) + + +# bug #20031 +proc main(a: uint64) = + case a + else: + discard + +static: + main(10) +main(10) diff --git a/tests/casestmt/tcstring.nim b/tests/casestmt/tcstring.nim new file mode 100644 index 000000000000..288373402d54 --- /dev/null +++ b/tests/casestmt/tcstring.nim @@ -0,0 +1,52 @@ +discard """ + targets: "c cpp js" +""" + +type Result = enum none, a, b, c, d, e, f + +proc foo1(x: cstring): Result = + const y = cstring"hash" + const arr = [cstring"it", cstring"finally"] + result = none + case x + of "Andreas", "Rumpf": result = a + of cstring"aa", "bb": result = b + of "cc", y, "when": result = c + of "will", arr, "be", "generated": result = d + of nil: result = f + +var results = [ + foo1("Rumpf"), foo1("Andreas"), + foo1("aa"), foo1(cstring"bb"), + foo1("cc"), foo1("hash"), + foo1("finally"), foo1("generated"), + foo1("no"), foo1("another no"), + foo1(nil)] +doAssert results == [a, a, b, b, c, c, d, d, none, none, f], $results + +proc foo2(x: cstring): Result = + const y = cstring"hash" + const arr = [cstring"it", cstring"finally"] + doAssert not (compiles do: + result = case x + of "Andreas", "Rumpf": a + of cstring"aa", "bb": b + of "cc", y, "when": c + of "will", arr, "be", "generated": d) + case x + of "Andreas", "Rumpf": a + of cstring"aa", "bb": b + of "cc", y, "when": c + of "will", arr, "be", "generated": d + of nil: f + else: e + +results = [ + foo2("Rumpf"), foo2("Andreas"), + foo2("aa"), foo2(cstring"bb"), + foo2("cc"), foo2("hash"), + foo2("finally"), foo2("generated"), + foo2("no"), foo2("another no"), + foo2(nil)] + +doAssert results == [a, a, b, b, c, c, d, d, e, e, f], $results diff --git a/tests/casestmt/tincompletecaseobject2.nim b/tests/casestmt/tincompletecaseobject2.nim index c080cfeb19b3..bbeae190978e 100644 --- a/tests/casestmt/tincompletecaseobject2.nim +++ b/tests/casestmt/tincompletecaseobject2.nim @@ -1,12 +1,5 @@ discard """ cmd: "nim check $file" -errormsg: "not all cases are covered; missing: {A, B}" -nimout: ''' -tincompletecaseobject2.nim(18, 1) Error: not all cases are covered; missing: {' ', '!', '\"', '#', '$', '%', '&', '\'', '*', '+', ',', '-', '.', '/', ':', ';', '<', '=', '>', '?', '@', '[', '\\', ']', '^', '_', '`', '{', '|', '}', '~'} -tincompletecaseobject2.nim(22, 1) Error: not all cases are covered; missing: {B, C, D} -tincompletecaseobject2.nim(25, 1) Error: not all cases are covered; missing: {A, C} -tincompletecaseobject2.nim(28, 1) Error: not all cases are covered; missing: {A, B} -''' """ type ABCD = enum A, B, C, D @@ -15,15 +8,19 @@ type AliasRangeABC = RangeABC PrintableChars = range[' ' .. '~'] -case PrintableChars 'x': +case PrintableChars 'x': #[tt.Error +^ not all cases are covered; missing: {' ', '!', '\"', '#', '$$', '%', '&', '\'', '*', '+', ',', '-', '.', '/', ':', ';', '<', '=', '>', '?', '@', '[', '\\', ']', '^', '_', '`', '{', '|', '}', '~'}]# of '0'..'9', 'A'..'Z', 'a'..'z': discard of '(', ')': discard -case AliasABCD A: +case AliasABCD A: #[tt.Error +^ not all cases are covered; missing: {B, C, D}]# of A: discard -case RangeABC A: +case RangeABC A: #[tt.Error +^ not all cases are covered; missing: {A, C}]# of B: discard -case AliasRangeABC A: +case AliasRangeABC A: #[tt.Error +^ not all cases are covered; missing: {A, B}]# of C: discard diff --git a/tests/ccgbugs/t13062.nim b/tests/ccgbugs/t13062.nim index fed32a1f7363..a8d2c1fec242 100644 --- a/tests/ccgbugs/t13062.nim +++ b/tests/ccgbugs/t13062.nim @@ -1,5 +1,5 @@ discard """ - output: "[p = nil]" + matrix: "--mm:refc; --mm:orc" targets: "c cpp" """ @@ -24,4 +24,7 @@ type fulfilled: Atomic[bool] var x: Pledge -echo x.repr +when defined(gcRefc): + doAssert x.repr == "[p = nil]" +elif not defined(cpp): # fixme # bug #20081 + doAssert x.repr == "Pledge(p: nil)" diff --git a/tests/ccgbugs/t20141.nim b/tests/ccgbugs/t20141.nim new file mode 100644 index 000000000000..499cd21aa032 --- /dev/null +++ b/tests/ccgbugs/t20141.nim @@ -0,0 +1,27 @@ +discard """ + joinable: false +""" + +# bug #20141 +type + A = object + B = object + U = proc() + +proc m(h: var B) = discard + +template n[T, U](x: U): T = + static: doAssert true + cast[ptr T](addr x)[] + +proc k() = + var res: A + m(n[B](res)) + +proc w(mounter: U) = discard + +proc mount(proto: U) = discard +proc v() = mount k + +# This is required for failure +w(v) \ No newline at end of file diff --git a/tests/ccgbugs/targ_lefttoright.nim b/tests/ccgbugs/targ_lefttoright.nim index a49b877391bd..a0adce1572a9 100644 --- a/tests/ccgbugs/targ_lefttoright.nim +++ b/tests/ccgbugs/targ_lefttoright.nim @@ -29,7 +29,7 @@ template test = var b = 1 say (b += 1; b), (b += 1; b) #2,3 - type C = object {.byRef.} + type C {.byRef.} = object i: int proc say(a, b: C) = diff --git a/tests/ccgbugs/tassign_nil_strings.nim b/tests/ccgbugs/tassign_nil_strings.nim index f6fab7baace0..e32bfcade61b 100644 --- a/tests/ccgbugs/tassign_nil_strings.nim +++ b/tests/ccgbugs/tassign_nil_strings.nim @@ -1,5 +1,5 @@ discard """ - cmd: "nim $target $options $file" + matrix: "--mm:refc" output: "Hello" ccodecheck: "\\i@'a = ((NimStringDesc*) NIM_NIL)'" """ diff --git a/tests/ccgbugs/tccgen1.nim b/tests/ccgbugs/tccgen1.nim index 4917c9848395..be571de0885a 100644 --- a/tests/ccgbugs/tccgen1.nim +++ b/tests/ccgbugs/tccgen1.nim @@ -7,7 +7,7 @@ type Features: seq[Feature] # Read-Only PNode* = ref Node - Node = object {.inheritable.} + Node {.inheritable.} = object attributes*: seq[PAttr] childNodes*: seq[PNode] FLocalName: string # Read-only diff --git a/tests/ccgbugs/tcodegenbug1.nim b/tests/ccgbugs/tcodegenbug1.nim index c62bae1efccb..d2ab97ede089 100644 --- a/tests/ccgbugs/tcodegenbug1.nim +++ b/tests/ccgbugs/tcodegenbug1.nim @@ -1,4 +1,5 @@ discard """ + matrix: "--mm:refc" output: '''obj = (inner: (kind: Just, id: 7)) obj.inner.id = 7 id = 7 diff --git a/tests/ccgbugs/tdeepcopy_addr_rval.nim b/tests/ccgbugs/tdeepcopy_addr_rval.nim index 07fb8f8ef75a..4a0b0deaac18 100644 --- a/tests/ccgbugs/tdeepcopy_addr_rval.nim +++ b/tests/ccgbugs/tdeepcopy_addr_rval.nim @@ -1,4 +1,5 @@ discard """ + matrix: "--mm:refc; --mm:orc --deepcopy:on" output: "3" """ diff --git a/tests/ccgbugs/tderefblock.nim b/tests/ccgbugs/tderefblock.nim new file mode 100644 index 000000000000..fd21a19b87bd --- /dev/null +++ b/tests/ccgbugs/tderefblock.nim @@ -0,0 +1,25 @@ +discard """ + cmd: "nim c -d:release -d:danger $file" + matrix: ";--gc:orc" + output: "42" +""" + +# bug #20107 + +type Foo = object + a, b, c, d: uint64 + +proc c(i: uint64): Foo = + Foo(a: i, b: i, c: i, d: i) + +func x(f: Foo): lent Foo {.inline.} = + f + +proc m() = + let f = block: + let i = c(42) + x(i) + + echo $f.a + +m() diff --git a/tests/ccgbugs/tforward_decl_only.nim b/tests/ccgbugs/tforward_decl_only.nim index 74fbae303dec..416e50eb531e 100644 --- a/tests/ccgbugs/tforward_decl_only.nim +++ b/tests/ccgbugs/tforward_decl_only.nim @@ -1,7 +1,7 @@ discard """ ccodecheck: "\\i !@('struct tyObject_MyRefObject'[0-z]+' {')" ccodecheck: "\\i !@('mymoduleInit')" -ccodecheck: "\\i @('mymoduleDatInit')" +ccodecheck: "\\i @('atmmymoduledotnim_DatInit000')" output: "hello" """ diff --git a/tests/ccgbugs/thtiobj.nim b/tests/ccgbugs/thtiobj.nim index 7a656905fbc9..6db24dad0da0 100644 --- a/tests/ccgbugs/thtiobj.nim +++ b/tests/ccgbugs/thtiobj.nim @@ -1,4 +1,5 @@ discard """ + matrix: "--mm:refc" targets: "c cpp" """ diff --git a/tests/ccgbugs/tmissingbracket.nim b/tests/ccgbugs/tmissingbracket.nim index 468e13366ed9..2919efe0e425 100644 --- a/tests/ccgbugs/tmissingbracket.nim +++ b/tests/ccgbugs/tmissingbracket.nim @@ -11,7 +11,7 @@ type className* : string TClassOfTobj = object of TClassOfTCustomObject nil - TCustomObject = ref object {.inheritable.} + TCustomObject {.inheritable.} = ref object class* : ptr TClassOfTCustomObject TObj = ref object of TCustomObject data: int diff --git a/tests/ccgbugs/tmissinginit.nim b/tests/ccgbugs/tmissinginit.nim index 8806a2f219da..9eb58221c52f 100644 --- a/tests/ccgbugs/tmissinginit.nim +++ b/tests/ccgbugs/tmissinginit.nim @@ -1,4 +1,5 @@ discard """ + matrix: "--mm:refc" output: '''0 0 0 diff --git a/tests/ccgbugs/tmissingvolatile.nim b/tests/ccgbugs/tmissingvolatile.nim index 1eccdc6b1b04..b877eff71c3a 100644 --- a/tests/ccgbugs/tmissingvolatile.nim +++ b/tests/ccgbugs/tmissingvolatile.nim @@ -1,6 +1,6 @@ discard """ output: "1" - cmd: r"nim c --hints:on $options -d:release $file" + cmd: r"nim c --hints:on $options --mm:refc -d:release $file" ccodecheck: "'NI volatile state;'" targets: "c" """ diff --git a/tests/closure/t11042.nim b/tests/closure/t11042.nim new file mode 100644 index 000000000000..6a39283160c4 --- /dev/null +++ b/tests/closure/t11042.nim @@ -0,0 +1,55 @@ +discard """ + output:''' +foo: 1 +foo: 2 +bar: 1 +bar: 2 +foo: 1 +foo: 2 +bar: 1 +bar: 2 +bar: 3 +bar: 4 +bar: 5 +bar: 6 +bar: 7 +bar: 8 +bar: 9 +''' +""" + +# bug #11042 +block: + iterator foo: int = + for x in 1..2: + echo "foo: ", x + for y in 1..2: + discard + + for x in foo(): discard + + let bar = iterator: int = + for x in 1..2: + echo "bar: ", x + for y in 1..2: + discard + + for x in bar(): discard + + +block: + iterator foo: int = + for x in 1..2: + echo "foo: ", x + for y in 1..2: + discard + + for x in foo(): discard + + let bar = iterator: int = + for x in 1..9: + echo "bar: ", x + for y in 1..2: + discard + + for x in bar(): discard \ No newline at end of file diff --git a/tests/collections/tseq.nim b/tests/collections/tseq.nim index a7a0c724ec45..c4dd40052a1e 100644 --- a/tests/collections/tseq.nim +++ b/tests/collections/tseq.nim @@ -1,4 +1,5 @@ discard """ + matrix: "--mm:refc; --mm:orc" output: ''' Hithere, what's your name?Hathere, what's your name? fA13msg1falsefB14msg2truefC15msg3false @@ -11,8 +12,6 @@ FilterIt: [1, 3, 7] Concat: [1, 3, 5, 7, 2, 4, 6] Deduplicate: [1, 2, 3, 4, 5, 7] @[()] -@[1, 42, 3] -@[1, 42, 3] 2345623456 ''' """ @@ -158,41 +157,41 @@ block tsequtils: echo someObjSeq - -block tshallowseq: - proc xxx() = - var x: seq[int] = @[1, 2, 3] - var y: seq[int] - system.shallowCopy(y, x) - y[1] = 42 - echo y - echo x - xxx() - - -block tshallowemptyseq: - proc test() = - var nilSeq: seq[int] = @[] - var emptySeq: seq[int] = newSeq[int]() - block: - var t = @[1,2,3] - shallow(nilSeq) - t = nilSeq - doAssert t == @[] - block: - var t = @[1,2,3] - shallow(emptySeq) - t = emptySeq - doAssert t == @[] - block: - var t = @[1,2,3] - shallowCopy(t, nilSeq) - doAssert t == @[] - block: - var t = @[1,2,3] - shallowCopy(t, emptySeq) - doAssert t == @[] - test() +when not defined(nimseqsv2): + block tshallowseq: + proc xxx() = + var x: seq[int] = @[1, 2, 3] + var y: seq[int] + system.shallowCopy(y, x) + y[1] = 42 + doAssert y == @[1, 42, 3] + doAssert x == @[1, 42, 3] + xxx() + + + block tshallowemptyseq: + proc test() = + var nilSeq: seq[int] = @[] + var emptySeq: seq[int] = newSeq[int]() + block: + var t = @[1,2,3] + shallow(nilSeq) + t = nilSeq + doAssert t == @[] + block: + var t = @[1,2,3] + shallow(emptySeq) + t = emptySeq + doAssert t == @[] + block: + var t = @[1,2,3] + shallowCopy(t, nilSeq) + doAssert t == @[] + block: + var t = @[1,2,3] + shallowCopy(t, emptySeq) + doAssert t == @[] + test() import strutils diff --git a/tests/compiler/samplelib.nim b/tests/compiler/samplelib.nim new file mode 100644 index 000000000000..c709717220ae --- /dev/null +++ b/tests/compiler/samplelib.nim @@ -0,0 +1,2 @@ +# Sample library used by tcmdlineclib.nim +proc test(): int {.cdecl, exportc, dynlib.} = 123 diff --git a/tests/compiler/tasm.nim b/tests/compiler/tasm.nim new file mode 100644 index 000000000000..9f60231e0bc0 --- /dev/null +++ b/tests/compiler/tasm.nim @@ -0,0 +1,15 @@ +proc testAsm() = + let src = 41 + var dst = 0 + + asm """ + mov %1, %0\n\t + add $1, %0 + : "=r" (`dst`) + : "r" (`src`)""" + + doAssert dst == 42 + +when defined(gcc) or defined(clang) and not defined(cpp): + {.passc: "-std=c99".} + testAsm() \ No newline at end of file diff --git a/tests/compiler/tcmdlineclib.nim b/tests/compiler/tcmdlineclib.nim new file mode 100644 index 000000000000..c06d6f103436 --- /dev/null +++ b/tests/compiler/tcmdlineclib.nim @@ -0,0 +1,3 @@ +proc test(): int {.importc, cdecl.} + +doAssert test() == 123 diff --git a/tests/compiler/tcmdlineclib.nims b/tests/compiler/tcmdlineclib.nims new file mode 100644 index 000000000000..f7899d92e7c9 --- /dev/null +++ b/tests/compiler/tcmdlineclib.nims @@ -0,0 +1,10 @@ +import os + +selfExec "c --app:lib " & (projectDir() / "samplelib.nim") +switch("clibdir", projectDir()) +--clib:samplelib + +# Make test executable can load sample shared library. +# `-rpath` option doesn't work and ignored on Windows. +# But the dll file in same directory as executable file is loaded. +switch("passL", "-Wl,-rpath," & projectDir()) diff --git a/tests/concepts/t18409.nim b/tests/concepts/t18409.nim new file mode 100644 index 000000000000..0edba2d31e48 --- /dev/null +++ b/tests/concepts/t18409.nim @@ -0,0 +1,37 @@ +discard """ + action: "compile" +""" + +# A vector space over a field F concept. +type VectorSpace*[F] = concept x, y, type V + vector_add(x, y) is V + scalar_mul(x, F) is V + dimension(V) is Natural + +# Real numbers (here floats) form a vector space. +func vector_add*(v: float, w: float): float = v + w +func scalar_mul*(v: float, s: float): float = v * s +func dimension*(x: typedesc[float]): Natural = 1 + +# 2-tuples of real numbers form a vector space. +func vector_add*(v, w: (float, float)): (float, float) = + (vector_add(v[0], w[0]), vector_add(v[1], w[1])) + +func scalar_mul*(v: (float, float), s: float): (float, float) = + (scalar_mul(v[0], s), scalar_mul(v[1], s)) + +func dimension*(x: typedesc[(float, float)]): Natural = 2 + +# Check concept requirements. +assert float is VectorSpace +assert (float, float) is VectorSpace + +# Commutivity axiom for vector spaces over the same field. +func axiom_commutivity*[F](u, v: VectorSpace[F]): bool = + vector_add(u, v) == vector_add(v, u) + +# This is okay. +assert axiom_commutivity(2.2, 3.3) + +# This is not. +assert axiom_commutivity((2.2, 3.3), (4.4, 5.5)) diff --git a/tests/concepts/t19730.nim b/tests/concepts/t19730.nim new file mode 100644 index 000000000000..575d45dda6bb --- /dev/null +++ b/tests/concepts/t19730.nim @@ -0,0 +1,20 @@ +discard """ + output: '''1.01.01.01.0 +1.01.01.01.0 +''' +""" + +type + Color = concept c + c.r is SomeFloat + c.g is SomeFloat + c.b is SomeFloat + c.a is SomeFloat + +proc useColor(color: Color) = + echo(color.r, color.g, color.b, color.a) + +let color = (r: 1.0, g: 1.0, b: 1.0, a: 1.0) +useColor(color) + +useColor((r: 1.0, g: 1.0, b: 1.0, a: 1.0)) diff --git a/tests/concepts/t3330.nim b/tests/concepts/t3330.nim index b92af5485395..901f8d2f40d5 100644 --- a/tests/concepts/t3330.nim +++ b/tests/concepts/t3330.nim @@ -1,4 +1,5 @@ discard """ +matrix: "--mm:refc" errormsg: "type mismatch: got " nimout: ''' t3330.nim(70, 4) Error: type mismatch: got @@ -48,7 +49,6 @@ expression: test(bar)''' - ## line 60 type Foo[T] = concept k diff --git a/tests/concepts/tusertypeclasses.nim b/tests/concepts/tusertypeclasses.nim index c7104f2a6c96..83e2b176eba4 100644 --- a/tests/concepts/tusertypeclasses.nim +++ b/tests/concepts/tusertypeclasses.nim @@ -1,4 +1,5 @@ discard """ + matrix: "--mm:refc" output: '''Sortable Sortable Container @@ -9,6 +10,8 @@ int ''' """ +# todo wait for https://github.com/nim-lang/Nim/pull/20380 + import typetraits template reject(expr) = assert(not compiles(x)) diff --git a/tests/config.nims b/tests/config.nims index 8c43055212ca..5195ebcafbc0 100644 --- a/tests/config.nims +++ b/tests/config.nims @@ -37,5 +37,8 @@ switch("define", "nimExperimentalLinenoiseExtra") # preview APIs are expected to be the new default in upcoming versions switch("define", "nimPreviewFloatRoundtrip") +switch("define", "nimPreviewDotLikeOps") switch("define", "nimPreviewJsonutilsHoleyEnum") switch("define", "nimPreviewHashRef") +when defined(windows): + switch("tlsEmulation", "off") diff --git a/tests/cpp/tdont_init_instantiation.nim b/tests/cpp/tdont_init_instantiation.nim index fe487fba085c..a13a3f6b4ed0 100644 --- a/tests/cpp/tdont_init_instantiation.nim +++ b/tests/cpp/tdont_init_instantiation.nim @@ -20,7 +20,7 @@ template class C { }; """.} -type C{.importcpp, header: "", nodecl.} [X] = object +type C[X] {.importcpp, header: "", nodecl.} = object proc mkC[X]: C[X] {.importcpp: "C<'*0>()", constructor, nodecl.} proc foo(): C[int] = diff --git a/tests/cpp/temitlist.nim b/tests/cpp/temitlist.nim index d446176fa477..9170be079643 100644 --- a/tests/cpp/temitlist.nim +++ b/tests/cpp/temitlist.nim @@ -8,7 +8,7 @@ disabled: "windows" # pending bug #18011 # bug #4730 -type Vector* {.importcpp: "std::vector", header: "".}[T] = object +type Vector*[T] {.importcpp: "std::vector", header: "".} = object template `[]=`*[T](v: var Vector[T], key: int, val: T) = {.emit: [v, "[", key, "] = ", val, ";"].} diff --git a/tests/cpp/torc.nim b/tests/cpp/torc.nim index 7fc474f94d4d..636105f31afa 100644 --- a/tests/cpp/torc.nim +++ b/tests/cpp/torc.nim @@ -12,4 +12,14 @@ type proc p(): Option[O] = none(O) -doAssert $p() == "none(O)" \ No newline at end of file +doAssert $p() == "none(O)" + +# bug #17351 +type + Foo = object of RootObj + Foo2 = object of Foo + Bar = object + x: Foo2 + +var b = Bar() +discard b diff --git a/tests/cpp/tretvar.nim b/tests/cpp/tretvar.nim index 83c37721ec09..0c3765346138 100644 --- a/tests/cpp/tretvar.nim +++ b/tests/cpp/tretvar.nim @@ -16,9 +16,9 @@ type proc c_str(a: stdString): cstring {.importcpp: "(char *)(#.c_str())", header: "".} -proc len(a: stdString): csize {.importcpp: "(#.length())", header: "".} +proc len(a: stdString): csize_t {.importcpp: "(#.length())", header: "".} -proc setChar(a: var stdString, i: csize, c: char) {.importcpp: "(#[#] = #)", header: "".} +proc setChar(a: var stdString, i: csize_t, c: char) {.importcpp: "(#[#] = #)", header: "".} proc `*`*[T](this: stdUniquePtr[T]): var T {.noSideEffect, importcpp: "(* #)", header: "".} diff --git a/tests/cpp/tvector_iterator.nim b/tests/cpp/tvector_iterator.nim index 4d686955fef8..c3886c547696 100644 --- a/tests/cpp/tvector_iterator.nim +++ b/tests/cpp/tvector_iterator.nim @@ -12,8 +12,8 @@ struct Vector { """.} type - Vector {.importcpp: "Vector".} [T] = object - VectorIterator {.importcpp: "Vector<'0>::Iterator".} [T] = object + Vector[T] {.importcpp: "Vector".} = object + VectorIterator[T] {.importcpp: "Vector<'0>::Iterator".} = object var x: VectorIterator[void] diff --git a/tests/deprecated/tconst.nim b/tests/deprecated/tconst.nim new file mode 100644 index 000000000000..51eb6cb0ec40 --- /dev/null +++ b/tests/deprecated/tconst.nim @@ -0,0 +1,8 @@ +discard """ + nimout: ''' +tconst.nim(8, 9) Warning: abcd; foo is deprecated [Deprecated] +''' +""" + +const foo* {.deprecated: "abcd".} = 42 +discard foo diff --git a/tests/destructor/t5342.nim b/tests/destructor/t5342.nim index 19354ea64d17..0acd5ef9d5e1 100644 --- a/tests/destructor/t5342.nim +++ b/tests/destructor/t5342.nim @@ -1,5 +1,6 @@ discard """ - matrix: "--gc:refc; --gc:arc" + matrix: "--mm:refc; --mm:arc" + targets: "c js" output: ''' 1 2 diff --git a/tests/destructor/tatomicptrs.nim b/tests/destructor/tatomicptrs.nim index 36f0cab8a99d..88f84d67c931 100644 --- a/tests/destructor/tatomicptrs.nim +++ b/tests/destructor/tatomicptrs.nim @@ -27,7 +27,7 @@ template decRef(x): untyped = atomicDec(x.refcount) proc makeShared*[T](x: sink T): SharedPtr[T] = # XXX could benefit from a macro that generates it. - result = cast[SharedPtr[T]](allocShared(sizeof(x))) + result = cast[SharedPtr[T]](allocShared0(sizeof(x))) result.x[] = x echo "allocating" diff --git a/tests/destructor/tcustomstrings.nim b/tests/destructor/tcustomstrings.nim index 119bfec2c5ce..31891856b926 100644 --- a/tests/destructor/tcustomstrings.nim +++ b/tests/destructor/tcustomstrings.nim @@ -8,8 +8,6 @@ after 20 20''' joinable: false """ -{.this: self.} - type mystring = object len, cap: int @@ -51,7 +49,7 @@ proc resize(self: var mystring) = if self.cap == 0: self.cap = 8 else: self.cap = (self.cap * 3) shr 1 if self.data == nil: inc allocCount - self.data = cast[type(data)](realloc(self.data, self.cap + 1)) + self.data = cast[type(self.data)](realloc(self.data, self.cap + 1)) proc add*(self: var mystring; c: char) = if self.len >= self.cap: resize(self) @@ -60,17 +58,17 @@ proc add*(self: var mystring; c: char) = inc self.len proc ensure(self: var mystring; newLen: int) = - if newLen >= cap: - cap = max((cap * 3) shr 1, newLen) - if cap > 0: - if data == nil: inc allocCount - data = cast[type(data)](realloc(data, cap + 1)) + if newLen >= self.cap: + self.cap = max((self.cap * 3) shr 1, newLen) + if self.cap > 0: + if self.data == nil: inc allocCount + self.data = cast[type(self.data)](realloc(self.data, self.cap + 1)) proc add*(self: var mystring; y: mystring) = - let newLen = len + y.len + let newLen = self.len + y.len ensure(self, newLen) - copyMem(addr data[len], y.data, y.data.len + 1) - len = newLen + copyMem(addr self.data[self.len], y.data, y.data.len + 1) + self.len = newLen proc create*(lit: string): mystring = let newLen = lit.len diff --git a/tests/destructor/tnewruntime_misc.nim b/tests/destructor/tnewruntime_misc.nim index ac061f2f7a1b..21c70557dbae 100644 --- a/tests/destructor/tnewruntime_misc.nim +++ b/tests/destructor/tnewruntime_misc.nim @@ -137,7 +137,16 @@ proc xx(xml: string): MyObject = discard xx("test") -echo getAllocStats() - s1 + +# Windows has 1 extra allocation in `getEnv` - there it allocates parameter to +# `_wgetenv` (WideCString). Therefore subtract by 1 to match other OSes' +# allocation. +when defined(windows): + import std/importutils + privateAccess(AllocStats) + echo getAllocStats() - s1 - AllocStats(allocCount: 1, deallocCount: 1) +else: + echo getAllocStats() - s1 # bug #13457 var s = "abcde" diff --git a/tests/destructor/trecursive.nim b/tests/destructor/trecursive.nim index 17a40e5a991d..e7afa6ba9733 100644 --- a/tests/destructor/trecursive.nim +++ b/tests/destructor/trecursive.nim @@ -47,7 +47,7 @@ proc `=destroy`(x: var MyObject) = proc `=`(x: var MyObject, y: MyObject) {.error.} proc newMyObject(i: int): MyObject = - result.p = create(int) + result.p = createShared(int) result.p[] = i proc test: seq[MyObject] = diff --git a/tests/distinct/tdistinct.nim b/tests/distinct/tdistinct.nim index dd8237854155..8ec083020810 100644 --- a/tests/distinct/tdistinct.nim +++ b/tests/distinct/tdistinct.nim @@ -135,11 +135,11 @@ block tRequiresInit: reject: var s: DistinctString s = "test" - doAssert s == "test" + doAssert string(s) == "test" accept: - let s = "test" - doAssert s == "test" + let s = DistinctString("test") + doAssert string(s) == "test" block: #17322 type diff --git a/tests/distinct/tnil.nim b/tests/distinct/tnil.nim index 5bdb97f37529..b54c07432da0 100644 --- a/tests/distinct/tnil.nim +++ b/tests/distinct/tnil.nim @@ -1,29 +1,21 @@ -discard """ -output: ''' -1 -0 -0 -''' -""" {.experimental: "notnil".} type MyPointer = distinct pointer MyString = distinct string MyInt = distinct int -proc foo(a: MyPointer) = +proc foo(a: MyPointer): int = # workaround a Windows 'repr' difference: - echo cast[int](a) + cast[int](a) -foo(cast[MyPointer](1)) -foo(cast[MyPointer](nil)) -foo(nil) +doAssert foo(cast[MyPointer](1)) == 1 +doAssert foo(cast[MyPointer](nil)) == 0 +doAssert foo(MyPointer(nil)) == 0 var p: MyPointer p = cast[MyPointer](1) p = cast[MyPointer](nil) p = nil.MyPointer -p = nil var i: MyInt i = 1.MyInt diff --git a/tests/effects/teffects1.nim b/tests/effects/teffects1.nim index 82efefe77d3a..ca9021999154 100644 --- a/tests/effects/teffects1.nim +++ b/tests/effects/teffects1.nim @@ -1,10 +1,8 @@ discard """ - cmd: "nim check $file" - nimout: '''teffects1.nim(22, 28) template/generic instantiation from here -teffects1.nim(23, 13) Error: can raise an unlisted exception: ref IOError -teffects1.nim(22, 29) Hint: 'lier' cannot raise 'IO2Error' [XCannotRaiseY] -teffects1.nim(38, 21) Error: type mismatch: got but expected 'MyProcType = proc (x: int): string{.closure.}' -.raise effects differ''' + cmd: "nim check --hint:Conf:off --hint:XDeclaredButNotUsed:off $file" + nimout: ''' +teffects1.nim(17, 28) template/generic instantiation from here +''' """ {.push warningAsError[Effect]: on.} type @@ -16,11 +14,10 @@ type proc forw: int {. .} -proc lier(): int {.raises: [IO2Error].} = - #[tt.Hint ^ 'lier' cannot raise 'IO2Error' [XCannotRaiseY] ]# +proc lier(): int {.raises: [IO2Error].} = #[tt.Hint + ^ 'lier' cannot raise 'IO2Error' [XCannotRaiseY] ]# writeLine stdout, "arg" #[tt.Error - ^ can raise an unlisted exception: ref IOError - ]# + ^ writeLine stdout, ["arg"] can raise an unlisted exception: ref IOError ]# proc forw: int = raise newException(IOError, "arg") @@ -30,15 +27,16 @@ proc forw: int = type MyProcType* = proc(x: int): string #{.raises: [ValueError, Defect].} -proc foo(x: int): string {.raises: [ValueError].} = +proc foo(x: int): string {.nimcall, raises: [ValueError].} = if x > 9: raise newException(ValueError, "Use single digit") $x var p: MyProcType = foo #[tt.Error ^ -type mismatch: got but expected 'MyProcType = proc (x: int): string{.closure.}' - +type mismatch: got but expected 'MyProcType = proc (x: int): string{.closure.}' + Calling convention mismatch: got '{.nimcall.}', but expected '{.closure.}'. +.raise effects differ ]# {.pop.} {.pop.} diff --git a/tests/effects/teffects11.nim b/tests/effects/teffects11.nim new file mode 100644 index 000000000000..e4098e9594f3 --- /dev/null +++ b/tests/effects/teffects11.nim @@ -0,0 +1,21 @@ +discard """ +action: compile +errormsg: "type mismatch: got " +line: 21 +""" + +type + Effect1 = object + Effect2 = object + Effect3 = object + +proc test(fnc: proc(x: int): void {.forbids: [Effect2].}) {.tags: [Effect1, Effect3, RootEffect].} = + fnc(1) + +proc t1(x: int): void = echo $x +proc t2(x: int): void {.tags: [Effect2].} = echo $x +proc t3(x: int): void {.tags: [Effect3].} = echo $x + +test(t1) +test(t3) +test(t2) diff --git a/tests/effects/teffects12.nim b/tests/effects/teffects12.nim new file mode 100644 index 000000000000..5f5499c3853b --- /dev/null +++ b/tests/effects/teffects12.nim @@ -0,0 +1,52 @@ +discard """ +action: compile +""" + +import std/locks + +type + Test2Effect* = object + Test2* = object + value2*: int + Test1Effect* = object + Test1* = object + value1*: int + Main* = object + test1Lock: Lock + test1: Test1 + test2Lock: Lock + test2: Test2 + +proc `=copy`(obj1: var Test2, obj2: Test2) {.error.} +proc `=copy`(obj1: var Test1, obj2: Test1) {.error.} +proc `=copy`(obj1: var Main, obj2: Main) {.error.} + +proc withTest1(main: var Main, + fn: proc(test1: var Test1) {.gcsafe, forbids: [Test1Effect].}) {.gcsafe, tags: [Test1Effect, RootEffect].} = + withLock(main.test1Lock): + fn(main.test1) + +proc withTest2(main: var Main, + fn: proc(test1: var Test2) {.gcsafe, forbids: [Test2Effect].}) {.gcsafe, tags: [Test2Effect, RootEffect].} = + withLock(main.test2Lock): + fn(main.test2) + +proc newMain(): Main = + var test1lock: Lock + initLock(test1Lock) + var test2lock: Lock + initLock(test2Lock) + var main = Main(test1Lock: move(test1Lock), test1: Test1(value1: 1), + test2Lock: move(test2Lock), test2: Test2(value2: 2)) + main.withTest1(proc(test1: var Test1) = test1.value1 += 1) + main.withTest2(proc(test2: var Test2) = test2.value2 += 1) + move main + +var main = newMain() +main.withTest1(proc(test1: var Test1) = + test1.value1 += 1 + main.withTest2(proc(test2: var Test2) = test2.value2 += 1) +) + +main.withTest1(proc(test1: var Test1) {.tags: [].} = echo $test1.value1) +main.withTest2(proc(test2: var Test2) {.tags: [].} = echo $test2.value2) diff --git a/tests/effects/teffects13.nim b/tests/effects/teffects13.nim new file mode 100644 index 000000000000..73082f99784e --- /dev/null +++ b/tests/effects/teffects13.nim @@ -0,0 +1,19 @@ +discard """ +action: compile +errormsg: "writeSomething() has an illegal effect: WriteIO" +line: 19 +""" + +type + IO = object of RootEffect ## input/output effect + ReadIO = object of IO ## input effect + WriteIO = object of IO ## output effect + +proc readSomething(): string {.tags: [ReadIO].} = "" +proc writeSomething(): void {.tags: [WriteIO].} = echo "..." + +proc noWritesPlease() {.forbids: [WriteIO].} = + # this is OK: + echo readSomething() + # the compiler prevents this: + writeSomething() diff --git a/tests/effects/teffects14.nim b/tests/effects/teffects14.nim new file mode 100644 index 000000000000..6291d95696dd --- /dev/null +++ b/tests/effects/teffects14.nim @@ -0,0 +1,15 @@ +discard """ +action: compile +errormsg: "func1() has an illegal effect: IO" +line: 15 +""" + +type IO = object ## input/output effect +proc func1(): string {.tags: [IO].} = discard +proc func2(): string = discard + +proc no_IO_please() {.forbids: [IO].} = + # this is OK because it didn't define any tag: + discard func2() + # the compiler prevents this: + let y = func1() diff --git a/tests/effects/teffects15.nim b/tests/effects/teffects15.nim new file mode 100644 index 000000000000..c3079cdbc1d7 --- /dev/null +++ b/tests/effects/teffects15.nim @@ -0,0 +1,18 @@ +discard """ +action: compile +errormsg: "method1(c) has an illegal effect: IO" +line: 18 +""" + +type + IO = object ## input/output effect + CustomObject* = object of RootObj + text: string + +method method1(obj: var CustomObject): string {.tags: [IO].} = obj.text & "." +method method2(obj: var CustomObject): string = obj.text & ":" + +proc noIO() {.forbids: [IO].} = + var c = CustomObject(text: "a") + echo c.method2() + echo c.method1() diff --git a/tests/effects/teffects16.nim b/tests/effects/teffects16.nim new file mode 100644 index 000000000000..ee8f782a3a89 --- /dev/null +++ b/tests/effects/teffects16.nim @@ -0,0 +1,20 @@ +discard """ +action: compile +errormsg: "writeSomething(\"a\") can have an unlisted effect: WriteIO" +line: 20 +""" + +type + IO = object of RootEffect ## input/output effect + ReadIO = object of IO ## input effect + WriteIO = object of IO ## output effect + LogIO = object of IO ## another output effect + +proc readSomething(): string {.tags: [ReadIO].} = "" +proc writeSomething(msg: string): void {.tags: [WriteIO].} = echo msg +proc logSomething(msg: string): void {.tags: [LogIo].} = echo msg + +proc noWritesPlease() {.forbids: [WriteIO], tags: [LogIO, ReadIO].} = + echo readSomething() + logSomething("a") + writeSomething("a") diff --git a/tests/effects/teffects17.nim b/tests/effects/teffects17.nim new file mode 100644 index 000000000000..5e6b838962d1 --- /dev/null +++ b/tests/effects/teffects17.nim @@ -0,0 +1,17 @@ +discard """ +action: compile +errormsg: "writeSomething(\"a\") has an illegal effect: WriteIO" +line: 17 +""" + +type + IO = object of RootEffect ## input/output effect + ReadIO = object of IO ## input effect + WriteIO = object of IO ## output effect + +proc readSomething(): string {.tags: [ReadIO].} = "" +proc writeSomething(msg: string): void {.tags: [WriteIO].} = echo msg + +proc illegalEffectNegation() {.forbids: [WriteIO], tags: [ReadIO, WriteIO].} = + echo readSomething() + writeSomething("a") diff --git a/tests/effects/teffects18.nim b/tests/effects/teffects18.nim new file mode 100644 index 000000000000..576e76635820 --- /dev/null +++ b/tests/effects/teffects18.nim @@ -0,0 +1,19 @@ +discard """ +action: compile +errormsg: "type mismatch: got " +line: 19 +""" + +type MyEffect = object +type ProcType1 = proc (i: int): void {.forbids: [MyEffect].} +type ProcType2 = proc (i: int): void + +proc testFunc(p: ProcType1): void = p(1) + +proc toBeCalled(i: int): void {.tags: [MyEffect].} = echo $i + +let emptyTags = proc(i: int): void {.tags: [].} = echo $i +let noTags: ProcType2 = proc(i: int): void = toBeCalled(i) + +testFunc(emptyTags) +testFunc(noTags) diff --git a/tests/effects/teffects19.nim b/tests/effects/teffects19.nim new file mode 100644 index 000000000000..49fb87523b39 --- /dev/null +++ b/tests/effects/teffects19.nim @@ -0,0 +1,23 @@ +discard """ +action: compile +errormsg: "type mismatch: got " +line: 23 +""" + +type MyEffect = object +type ProcType1 = proc (i: int): void {.forbids: [MyEffect].} +type ProcType2 = proc (i: int): void + +proc caller1(p: ProcType1): void = p(1) +proc caller2(p: ProcType2): void = p(1) + +proc effectful(i: int): void {.tags: [MyEffect].} = echo $i +proc effectless(i: int): void {.forbids: [MyEffect].} = echo $i + +proc toBeCalled1(i: int): void = effectful(i) +proc toBeCalled2(i: int): void = effectless(i) + +caller1(toBeCalled2) +caller2(toBeCalled1) +caller2(toBeCalled2) +caller1(toBeCalled1) diff --git a/tests/effects/teffects2.nim b/tests/effects/teffects2.nim index ec3c0a887380..777a4cebc528 100644 --- a/tests/effects/teffects2.nim +++ b/tests/effects/teffects2.nim @@ -4,7 +4,7 @@ discard """ """ {.push warningAsError[Effect]: on.} type - TObj = object {.pure, inheritable.} + TObj {.pure, inheritable.} = object TObjB = object of TObj a, b, c: string diff --git a/tests/effects/teffects3.nim b/tests/effects/teffects3.nim index ee5470c47c36..4c050510a078 100644 --- a/tests/effects/teffects3.nim +++ b/tests/effects/teffects3.nim @@ -4,7 +4,7 @@ discard """ """ type - TObj = object {.pure, inheritable.} + TObj {.pure, inheritable.} = object TObjB = object of TObj a, b, c: string fn: proc (): int {.tags: [].} diff --git a/tests/effects/teffects4.nim b/tests/effects/teffects4.nim index 88cc0efa930a..b875754b6bab 100644 --- a/tests/effects/teffects4.nim +++ b/tests/effects/teffects4.nim @@ -4,7 +4,7 @@ discard """ """ type - TObj = object {.pure, inheritable.} + TObj {.pure, inheritable.} = object TObjB = object of TObj a, b, c: string fn: proc (): int {.tags: [ReadIOEffect].} diff --git a/tests/effects/tstrict_effects3.nim b/tests/effects/tstrict_effects3.nim index 4cc46154940b..027b4647413a 100644 --- a/tests/effects/tstrict_effects3.nim +++ b/tests/effects/tstrict_effects3.nim @@ -27,3 +27,20 @@ func mkEnter() = helper() else: let ast = getAst(helper()) + + +# bug #6559 +type + SafeFn = proc (): void {. raises: [] } + +proc ok() {. raises: [] .} = discard +proc fail() {. raises: [] .} + +let f1 : SafeFn = ok +let f2 : SafeFn = fail + + +proc fail() = discard +f1() +f2() + diff --git a/tests/enum/mregression.nim b/tests/enum/mcrossmodule.nim similarity index 52% rename from tests/enum/mregression.nim rename to tests/enum/mcrossmodule.nim index 9c3e3cf8bf37..e534a202c2ca 100644 --- a/tests/enum/mregression.nim +++ b/tests/enum/mcrossmodule.nim @@ -2,3 +2,5 @@ type OtherEnum* = enum Success, Failed, More + +proc some*(x: OtherEnum): bool = x == Success diff --git a/tests/enum/tcrossmodule.nim b/tests/enum/tcrossmodule.nim new file mode 100644 index 000000000000..1e97fd1ee49d --- /dev/null +++ b/tests/enum/tcrossmodule.nim @@ -0,0 +1,10 @@ +import mcrossmodule + +type + MyEnum = enum + Success + +template t = + doAssert some(Success) + +t() diff --git a/tests/enum/toverloadable_enums.nim b/tests/enum/toverloadable_enums.nim index df255dd71c94..9bb5514674ca 100644 --- a/tests/enum/toverloadable_enums.nim +++ b/tests/enum/toverloadable_enums.nim @@ -84,3 +84,37 @@ proc g3[T](x: T, e: E2): int = of value2: echo "E2-B" let v5 = g3(99, E2.value2) + +block: # only allow enums to overload enums + # mirrors behavior without overloadableEnums + proc foo() = discard + block: + type Foo = enum foo + doAssert foo is Foo + foo() + +import macros +block: # test with macros/templates + type + Enum1 = enum + value01, value02 + Enum2 = enum + value01, value10 + + macro isOneM(a: untyped): bool = + result = newCall(bindSym"==", a, ident"value01") + + macro isOneMS(a: untyped): bool = + result = newCall(bindSym"==", a, bindSym"value01") + + template isOneT(a: untyped): bool = + a == value01 + + let e1 = Enum1.value01 + let e2 = Enum2.value01 + doAssert isOneM(e1) + doAssert isOneM(e2) + doAssert isOneMS(e1) + doAssert isOneMS(e2) + doAssert isOneT(e1) + doAssert isOneT(e2) diff --git a/tests/enum/tregression.nim b/tests/enum/tregression.nim deleted file mode 100644 index 597f0c80aa08..000000000000 --- a/tests/enum/tregression.nim +++ /dev/null @@ -1,13 +0,0 @@ -discard """ - action: "compile" -""" -import options, mregression - -type - MyEnum = enum - Success - -template t = - echo some(Success) - -t() diff --git a/tests/errmsgs/t10376.nim b/tests/errmsgs/t10376.nim index 2ce16d6a224b..814c860dc820 100644 --- a/tests/errmsgs/t10376.nim +++ b/tests/errmsgs/t10376.nim @@ -1,6 +1,7 @@ discard """ + matrix: "--mm:refc" errormsg: "finalizer must be a direct reference to a proc" - line: 29 + line: 30 """ type diff --git a/tests/errmsgs/t18983.nim b/tests/errmsgs/t18983.nim new file mode 100644 index 000000000000..3451875cb4c6 --- /dev/null +++ b/tests/errmsgs/t18983.nim @@ -0,0 +1,7 @@ +discard """ + errormsg: "illegal recursion in type 'A'" +""" + +type + A* = A + B = (A,) diff --git a/tests/errmsgs/t19882.nim b/tests/errmsgs/t19882.nim new file mode 100644 index 000000000000..1f2f95ab738c --- /dev/null +++ b/tests/errmsgs/t19882.nim @@ -0,0 +1,10 @@ + +discard """ + errormsg: "cannot instantiate 'A[T, P]' inside of type definition: 'init'; Maybe generic arguments are missing?" +""" +type A[T,P] = object + b:T + c:P +proc init(): ref A = + new(result) +var a = init() diff --git a/tests/errmsgs/t19882_2.nim b/tests/errmsgs/t19882_2.nim new file mode 100644 index 000000000000..7f3055a5da73 --- /dev/null +++ b/tests/errmsgs/t19882_2.nim @@ -0,0 +1,5 @@ +discard """ + errormsg: "cannot instantiate: 'A[T]'; the object's generic parameters cannot be inferred and must be explicitly given" +""" +type A[T] = object +var a = A() \ No newline at end of file diff --git a/tests/errmsgs/t2614.nim b/tests/errmsgs/t2614.nim new file mode 100644 index 000000000000..4034249e7870 --- /dev/null +++ b/tests/errmsgs/t2614.nim @@ -0,0 +1,21 @@ +discard """ + cmd: "nim check $options --hints:off $file" + errormsg: "" + nimout: ''' +t2614.nim(19, 27) Error: type mismatch: got .}]> but expected 'array[0..1, proc (){.closure.}]' + Calling convention mismatch: got '{.nimcall.}', but expected '{.closure.}'. +t2614.nim(21, 22) Error: type mismatch: got .}]> but expected 'seq[proc (){.closure.}]' + Calling convention mismatch: got '{.nimcall.}', but expected '{.closure.}'. +''' +""" + +proc g +proc f = + if false: g() +proc g = + if false: f() + +var a = [f, g] # This works +var b: array[2, proc()] = [f, g] # Error + +var c: seq[proc()] = @[f, g] \ No newline at end of file diff --git a/tests/errmsgs/t5282.nim b/tests/errmsgs/t5282.nim new file mode 100644 index 000000000000..4da49d1551b7 --- /dev/null +++ b/tests/errmsgs/t5282.nim @@ -0,0 +1,5 @@ +discard """ + errormsg: "illegal recursion in type 'x'" +""" + +type x = distinct x diff --git a/tests/errmsgs/t8064.nim b/tests/errmsgs/t8064.nim new file mode 100644 index 000000000000..10bb86299388 --- /dev/null +++ b/tests/errmsgs/t8064.nim @@ -0,0 +1,6 @@ +discard """ + errormsg: "expression has no type: values" +""" +import tables + +values \ No newline at end of file diff --git a/tests/errmsgs/t9908_01.nim b/tests/errmsgs/t9908_01.nim index b9d37b67b7d8..99bc8237d85c 100644 --- a/tests/errmsgs/t9908_01.nim +++ b/tests/errmsgs/t9908_01.nim @@ -1,5 +1,5 @@ discard """ -errormsg: "ordinal type expected" +errormsg: "ordinal type expected; given: string" line: 10 """ diff --git a/tests/errmsgs/t9908_02.nim b/tests/errmsgs/t9908_02.nim index 7ff3d1ff73ec..4fc60b3df197 100644 --- a/tests/errmsgs/t9908_02.nim +++ b/tests/errmsgs/t9908_02.nim @@ -1,5 +1,5 @@ discard """ -errormsg: "ordinal type expected" +errormsg: "ordinal type expected; given: float" line: 10 """ diff --git a/tests/errmsgs/tcannot_capture_builtin.nim b/tests/errmsgs/tcannot_capture_builtin.nim index 3b8aae2417a8..65afa627e401 100644 --- a/tests/errmsgs/tcannot_capture_builtin.nim +++ b/tests/errmsgs/tcannot_capture_builtin.nim @@ -1,5 +1,5 @@ discard """ -errormsg: "'+' cannot be passed to a procvar" +errormsg: "'+' is a built-in and cannot be used as a first-class procedure" line: 8 """ diff --git a/tests/errmsgs/tdistinct_nil.nim b/tests/errmsgs/tdistinct_nil.nim new file mode 100644 index 000000000000..a2b403293bf0 --- /dev/null +++ b/tests/errmsgs/tdistinct_nil.nim @@ -0,0 +1,23 @@ +discard """ + cmd: "nim check $file" + action: reject + nimout: ''' +tdistinct_nil.nim(23, 4) Error: type mismatch: got +but expected one of: +proc foo(x: DistinctPointer) + first type mismatch at position: 1 + required type for x: DistinctPointer + but expression 'nil' is of type: typeof(nil) + +expression: foo(nil) +''' +""" + +type + DistinctPointer = distinct pointer + +proc foo(x: DistinctPointer) = + discard + +foo(DistinctPointer(nil)) +foo(nil) diff --git a/tests/errmsgs/tinteger_literals.nim b/tests/errmsgs/tinteger_literals.nim index 98c92a2277ab..f90ab14a17f8 100644 --- a/tests/errmsgs/tinteger_literals.nim +++ b/tests/errmsgs/tinteger_literals.nim @@ -1,15 +1,14 @@ discard """ -cmd: "nim check $file" -errormsg: "number out of range: '300'u8'" -nimout: ''' -tinteger_literals.nim(12, 9) Error: number out of range: '18446744073709551616'u64' -tinteger_literals.nim(13, 9) Error: number out of range: '9223372036854775808'i64' -tinteger_literals.nim(14, 9) Error: number out of range: '9223372036854775808' -tinteger_literals.nim(15, 9) Error: number out of range: '300'u8' -''' + cmd: "nim check $file" """ - -discard 18446744073709551616'u64 # high(uint64) + 1 -discard 9223372036854775808'i64 # high(int64) + 1 -discard 9223372036854775808 # high(int64) + 1 -discard 300'u8 \ No newline at end of file +# high(uint64) + 1 +discard 18446744073709551616'u64 #[tt.Error + ^ number out of range: '18446744073709551616'u64' ]# +# high(int64) + 1 +discard 9223372036854775808'i64 #[tt.Error + ^ number out of range: '9223372036854775808'i64' ]# +# high(int64) + 1 +discard 9223372036854775808 #[tt.Error + ^ number out of range: '9223372036854775808' ]# +discard 300'u8 #[tt.Error + ^ number out of range: '300'u8' ]# diff --git a/tests/errmsgs/tinvalidinout.nim b/tests/errmsgs/tinvalidinout.nim index 1fa3805ee70b..268bcf4d516e 100644 --- a/tests/errmsgs/tinvalidinout.nim +++ b/tests/errmsgs/tinvalidinout.nim @@ -9,7 +9,7 @@ tinvalidinout.nim(18, 9) Error: the 'in' modifier can be used only with imported """ type - Foo {.header: "foo.h", importcpp.} [in T] = object + Foo[in T] {.header: "foo.h", importcpp.} = object Bar[out X] = object x: int diff --git a/tests/errmsgs/tproc_mismatch.nim b/tests/errmsgs/tproc_mismatch.nim index 4ddc7635ec55..f92589d96578 100644 --- a/tests/errmsgs/tproc_mismatch.nim +++ b/tests/errmsgs/tproc_mismatch.nim @@ -3,9 +3,9 @@ discard """ cmd: '''nim check --hints:off $options $file''' nimoutFull: true nimout: ''' -tproc_mismatch.nim(35, 52) Error: type mismatch: got but expected 'proc (a: int, c: float){.closure, noSideEffect.}' +tproc_mismatch.nim(38, 52) Error: type mismatch: got but expected 'proc (a: int, c: float){.closure, noSideEffect.}' Calling convention mismatch: got '{.cdecl.}', but expected '{.closure.}'. -tproc_mismatch.nim(39, 6) Error: type mismatch: got +tproc_mismatch.nim(42, 6) Error: type mismatch: got but expected one of: proc bar(a: proc ()) first type mismatch at position: 1 @@ -14,18 +14,21 @@ proc bar(a: proc ()) Calling convention mismatch: got '{.inline.}', but expected '{.closure.}'. expression: bar(fn1) -tproc_mismatch.nim(43, 8) Error: type mismatch: got but expected 'proc (){.closure.}' +tproc_mismatch.nim(46, 8) Error: type mismatch: got but expected 'proc (){.closure.}' Calling convention mismatch: got '{.inline.}', but expected '{.closure.}'. -tproc_mismatch.nim(48, 8) Error: type mismatch: got but expected 'proc (){.closure, noSideEffect.}' +tproc_mismatch.nim(51, 8) Error: type mismatch: got but expected 'proc (){.closure, noSideEffect.}' + Calling convention mismatch: got '{.nimcall.}', but expected '{.closure.}'. Pragma mismatch: got '{..}', but expected '{.noSideEffect.}'. -tproc_mismatch.nim(52, 8) Error: type mismatch: got but expected 'proc (a: float){.closure.}' -tproc_mismatch.nim(61, 9) Error: type mismatch: got but expected 'proc (a: int){.closure, gcsafe.}' +tproc_mismatch.nim(55, 8) Error: type mismatch: got but expected 'proc (a: float){.closure.}' + Calling convention mismatch: got '{.nimcall.}', but expected '{.closure.}'. +tproc_mismatch.nim(64, 9) Error: type mismatch: got but expected 'proc (a: int){.closure, gcsafe.}' + Calling convention mismatch: got '{.nimcall.}', but expected '{.closure.}'. Pragma mismatch: got '{..}', but expected '{.gcsafe.}'. -tproc_mismatch.nim(69, 9) Error: type mismatch: got but expected 'proc (a: int): int{.cdecl.}' +tproc_mismatch.nim(72, 9) Error: type mismatch: got but expected 'proc (a: int): int{.cdecl.}' Calling convention mismatch: got '{.nimcall.}', but expected '{.cdecl.}'. -tproc_mismatch.nim(70, 9) Error: type mismatch: got but expected 'proc (a: int): int{.nimcall.}' +tproc_mismatch.nim(73, 9) Error: type mismatch: got but expected 'proc (a: int): int{.nimcall.}' Calling convention mismatch: got '{.cdecl.}', but expected '{.nimcall.}'. -tproc_mismatch.nim(74, 9) Error: type mismatch: got but expected 'proc (a: int){.closure, locks: 1.}' +tproc_mismatch.nim(77, 9) Error: type mismatch: got but expected 'proc (a: int){.closure, locks: 1.}' Pragma mismatch: got '{.locks: 3.}', but expected '{.locks: 1.}'. lock levels differ ''' diff --git a/tests/errmsgs/treportunused.nim b/tests/errmsgs/treportunused.nim index f5ee79afad99..46afe163d6a8 100644 --- a/tests/errmsgs/treportunused.nim +++ b/tests/errmsgs/treportunused.nim @@ -2,19 +2,27 @@ discard """ matrix: "--hint:all:off --hint:XDeclaredButNotUsed" nimoutFull: true nimout: ''' -treportunused.nim(23, 10) Hint: 's1' is declared but not used [XDeclaredButNotUsed] -treportunused.nim(24, 10) Hint: 's2' is declared but not used [XDeclaredButNotUsed] -treportunused.nim(25, 10) Hint: 's3' is declared but not used [XDeclaredButNotUsed] -treportunused.nim(26, 6) Hint: 's4' is declared but not used [XDeclaredButNotUsed] -treportunused.nim(27, 6) Hint: 's5' is declared but not used [XDeclaredButNotUsed] -treportunused.nim(28, 7) Hint: 's6' is declared but not used [XDeclaredButNotUsed] -treportunused.nim(29, 7) Hint: 's7' is declared but not used [XDeclaredButNotUsed] -treportunused.nim(30, 5) Hint: 's8' is declared but not used [XDeclaredButNotUsed] -treportunused.nim(31, 5) Hint: 's9' is declared but not used [XDeclaredButNotUsed] -treportunused.nim(32, 6) Hint: 's10' is declared but not used [XDeclaredButNotUsed] -treportunused.nim(33, 6) Hint: 's11' is declared but not used [XDeclaredButNotUsed] -treportunused.nim(37, 3) Hint: 'v0.99' is declared but not used [XDeclaredButNotUsed] -treportunused.nim(38, 3) Hint: 'v0.99.99' is declared but not used [XDeclaredButNotUsed] +treportunused.nim(51, 5) Hint: 'A' is declared but not used [XDeclaredButNotUsed] +treportunused.nim(52, 5) Hint: 'B' is declared but not used [XDeclaredButNotUsed] +treportunused.nim(55, 5) Hint: 'D' is declared but not used [XDeclaredButNotUsed] +treportunused.nim(56, 5) Hint: 'E' is declared but not used [XDeclaredButNotUsed] +treportunused.nim(59, 5) Hint: 'G' is declared but not used [XDeclaredButNotUsed] +treportunused.nim(60, 5) Hint: 'H' is declared but not used [XDeclaredButNotUsed] +treportunused.nim(64, 5) Hint: 'K' is declared but not used [XDeclaredButNotUsed] +treportunused.nim(65, 5) Hint: 'L' is declared but not used [XDeclaredButNotUsed] +treportunused.nim(31, 10) Hint: 's1' is declared but not used [XDeclaredButNotUsed] +treportunused.nim(32, 10) Hint: 's2' is declared but not used [XDeclaredButNotUsed] +treportunused.nim(33, 10) Hint: 's3' is declared but not used [XDeclaredButNotUsed] +treportunused.nim(34, 6) Hint: 's4' is declared but not used [XDeclaredButNotUsed] +treportunused.nim(35, 6) Hint: 's5' is declared but not used [XDeclaredButNotUsed] +treportunused.nim(36, 7) Hint: 's6' is declared but not used [XDeclaredButNotUsed] +treportunused.nim(37, 7) Hint: 's7' is declared but not used [XDeclaredButNotUsed] +treportunused.nim(38, 5) Hint: 's8' is declared but not used [XDeclaredButNotUsed] +treportunused.nim(39, 5) Hint: 's9' is declared but not used [XDeclaredButNotUsed] +treportunused.nim(40, 6) Hint: 's10' is declared but not used [XDeclaredButNotUsed] +treportunused.nim(41, 6) Hint: 's11' is declared but not used [XDeclaredButNotUsed] +treportunused.nim(45, 3) Hint: 'v0.99' is declared but not used [XDeclaredButNotUsed] +treportunused.nim(46, 3) Hint: 'v0.99.99' is declared but not used [XDeclaredButNotUsed] ''' action: compile """ @@ -36,3 +44,22 @@ type s11 = type(1.2) let `v0.99` = "0.99" `v0.99.99` = "0.99.99" + +block: # bug #18201 + # Test that unused type aliases raise hint XDeclaredButNotUsed. + type + A = int + B = distinct int + + C = object + D = C + E = distinct C + + F = string + G = F + H = distinct F + + J = enum + Foo + K = J + L = distinct J diff --git a/tests/exception/tcpp_imported_exc.nim b/tests/exception/tcpp_imported_exc.nim index 5195b48e7a11..55a58440ffb5 100644 --- a/tests/exception/tcpp_imported_exc.nim +++ b/tests/exception/tcpp_imported_exc.nim @@ -1,4 +1,5 @@ discard """ +matrix: "--mm:refc" targets: "cpp" output: ''' caught as std::exception @@ -76,7 +77,7 @@ doAssert(getCurrentException() == nil) # raise by pointer and also generic type type - std_vector {.importcpp"std::vector", header"".} [T] = object + std_vector[T] {.importcpp"std::vector", header"".} = object proc newVector[T](len: int): ptr std_vector[T] {.importcpp: "new std::vector<'1>(@)".} proc deleteVector[T](v: ptr std_vector[T]) {.importcpp: "delete @; @ = NIM_NIL;".} diff --git a/tests/gc/cyclecollector.nim b/tests/gc/cyclecollector.nim index 7b47758f235f..2d02a7a3c434 100644 --- a/tests/gc/cyclecollector.nim +++ b/tests/gc/cyclecollector.nim @@ -9,7 +9,10 @@ type proc createCycle(leaf: string): Node = new result result.a = result - shallowCopy result.leaf, leaf + when defined(gcArc) or defined(gcOrc): + result.leaf = leaf + else: + shallowCopy result.leaf, leaf proc main = for i in 0 .. 100_000: diff --git a/tests/generics/module_with_generics.nim b/tests/generics/module_with_generics.nim index e801a4790e66..960a694d7ca4 100644 --- a/tests/generics/module_with_generics.nim +++ b/tests/generics/module_with_generics.nim @@ -1,5 +1,5 @@ type - Base[T] = ref object {.inheritable.} + Base[T] {.inheritable.} = ref object value*: T Derived[T] = ref object of Base[T] diff --git a/tests/generics/tcan.nim b/tests/generics/tcan.nim index eea69cdb7a0b..bbefa72331ac 100644 --- a/tests/generics/tcan.nim +++ b/tests/generics/tcan.nim @@ -31,7 +31,7 @@ block tinherit: block tspecialise: type - TGen[T] = object {.inheritable.} + TGen[T] {.inheritable.} = object TSpef = object of TGen[string] diff --git a/tests/generics/tgeneric3.nim b/tests/generics/tgeneric3.nim index 4cb12f91ba69..07ab822ae4a7 100644 --- a/tests/generics/tgeneric3.nim +++ b/tests/generics/tgeneric3.nim @@ -12,7 +12,7 @@ import strutils type PNode[T,D] = ref TNode[T,D] - TItem {.acyclic, pure, final, shallow.} [T,D] = object + TItem[T,D] {.acyclic, pure, final, shallow.} = object key: T value: D node: PNode[T,D] @@ -20,7 +20,7 @@ type val_set: bool TItems[T,D] = seq[ref TItem[T,D]] - TNode {.acyclic, pure, final, shallow.} [T,D] = object + TNode[T,D] {.acyclic, pure, final, shallow.} = object slots: TItems[T,D] left: PNode[T,D] count: int32 @@ -243,7 +243,7 @@ proc InsertItem[T,D](APath: RPath[T,D], ANode:PNode[T,D], Akey: T, Avalue: D) = of cLenCenter: setLen(APath.Nd.slots, cLen4) of cLen4: setLen(APath.Nd.slots, cLenMax) else: discard - for i in countdown(APath.Nd.count.int - 1, x + 1): shallowCopy(APath.Nd.slots[i], APath.Nd.slots[i - 1]) + for i in countdown(APath.Nd.count.int - 1, x + 1): APath.Nd.slots[i] = move APath.Nd.slots[i - 1] APath.Nd.slots[x] = setItem(Akey, Avalue, ANode) @@ -255,31 +255,39 @@ proc SplitPage[T,D](n, left: PNode[T,D], xi: int, Akey:var T, Avalue:var D): PNo result.slots.newSeq(cLenCenter) result.count = cCenter if x == cCenter: - for i in 0..cCenter-1: shallowCopy(it1[i], left.slots[i]) - for i in 0..cCenter-1: shallowCopy(result.slots[i], left.slots[cCenter + i]) + for i in 0..cCenter-1: + it1[i] = move left.slots[i] + for i in 0..cCenter-1: + result.slots[i] = move left.slots[cCenter + i] result.left = n else : if x < cCenter : - for i in 0..x-1: shallowCopy(it1[i], left.slots[i]) + for i in 0..x-1: + it1[i] = move left.slots[i] it1[x] = setItem(Akey, Avalue, n) - for i in x+1 .. cCenter-1: shallowCopy(it1[i], left.slots[i-1]) + for i in x+1 .. cCenter-1: + it1[i] = move left.slots[i-1] var w = left.slots[cCenter-1] Akey = w.key Avalue = w.value result.left = w.node - for i in 0..cCenter-1: shallowCopy(result.slots[i], left.slots[cCenter + i]) + for i in 0..cCenter-1: + result.slots[i] = move left.slots[cCenter + i] else : - for i in 0..cCenter-1: shallowCopy(it1[i], left.slots[i]) + for i in 0..cCenter-1: + it1[i] = move left.slots[i] x = x - (cCenter + 1) - for i in 0..x-1: shallowCopy(result.slots[i], left.slots[cCenter + i + 1]) + for i in 0..x-1: + result.slots[i] = move left.slots[cCenter + i + 1] result.slots[x] = setItem(Akey, Avalue, n) - for i in x+1 .. cCenter-1: shallowCopy(result.slots[i], left.slots[cCenter + i]) + for i in x+1 .. cCenter-1: + result.slots[i] = move left.slots[cCenter + i] var w = left.slots[cCenter] Akey = w.key Avalue = w.value result.left = w.node left.count = cCenter - shallowCopy(left.slots, it1) + left.slots = move it1 proc internalPut[T,D](ANode: ref TNode[T,D], Akey: T, Avalue: D, Oldvalue: var D): ref TNode[T,D] = diff --git a/tests/generics/tgeneric_recursionlimit.nim b/tests/generics/tgeneric_recursionlimit.nim new file mode 100644 index 000000000000..5fe9b43c6e73 --- /dev/null +++ b/tests/generics/tgeneric_recursionlimit.nim @@ -0,0 +1,123 @@ +discard """ + action: "compile" +""" + +# https://github.com/nim-lang/Nim/issues/20348 + +type + Payload[T] = object + payload: T + Carrier[T] = object + val: T + +type + Payload0*[T] = object + payload: Payload[T] + Payload1*[T] = object + payload: Payload[T] + Payload2*[T] = object + payload: Payload[T] + Payload3*[T] = object + payload: Payload[T] + Payload4*[T] = object + payload: Payload[T] + Payload5*[T] = object + payload: Payload[T] + Payload6*[T] = object + payload: Payload[T] + Payload7*[T] = object + payload: Payload[T] + Payload8*[T] = object + payload: Payload[T] + Payload9*[T] = object + payload: Payload[T] + Payload10*[T] = object + payload: Payload[T] + Payload11*[T] = object + payload: Payload[T] + Payload12*[T] = object + payload: Payload[T] + Payload13*[T] = object + payload: Payload[T] + Payload14*[T] = object + payload: Payload[T] + Payload15*[T] = object + payload: Payload[T] + Payload16*[T] = object + payload: Payload[T] + Payload17*[T] = object + payload: Payload[T] + Payload18*[T] = object + payload: Payload[T] + Payload19*[T] = object + payload: Payload[T] + Payload20*[T] = object + payload: Payload[T] + Payload21*[T] = object + payload: Payload[T] + Payload22*[T] = object + payload: Payload[T] + Payload23*[T] = object + payload: Payload[T] + Payload24*[T] = object + payload: Payload[T] + Payload25*[T] = object + payload: Payload[T] + Payload26*[T] = object + payload: Payload[T] + Payload27*[T] = object + payload: Payload[T] + Payload28*[T] = object + payload: Payload[T] + Payload29*[T] = object + payload: Payload[T] + Payload30*[T] = object + payload: Payload[T] + Payload31*[T] = object + payload: Payload[T] + Payload32*[T] = object + payload: Payload[T] + Payload33*[T] = object + payload: Payload[T] + +type + Carriers*[T] = object + c0*: Carrier[Payload0[T]] + c1*: Carrier[Payload1[T]] + c2*: Carrier[Payload2[T]] + c3*: Carrier[Payload3[T]] + c4*: Carrier[Payload4[T]] + c5*: Carrier[Payload5[T]] + c6*: Carrier[Payload6[T]] + c7*: Carrier[Payload7[T]] + c8*: Carrier[Payload8[T]] + c9*: Carrier[Payload9[T]] + c10*: Carrier[Payload10[T]] + c11*: Carrier[Payload11[T]] + c12*: Carrier[Payload12[T]] + c13*: Carrier[Payload13[T]] + c14*: Carrier[Payload14[T]] + c15*: Carrier[Payload15[T]] + c16*: Carrier[Payload16[T]] + c17*: Carrier[Payload17[T]] + c18*: Carrier[Payload18[T]] + c19*: Carrier[Payload19[T]] + c20*: Carrier[Payload20[T]] + c21*: Carrier[Payload21[T]] + c22*: Carrier[Payload22[T]] + c23*: Carrier[Payload23[T]] + c24*: Carrier[Payload24[T]] + c25*: Carrier[Payload25[T]] + c26*: Carrier[Payload26[T]] + c27*: Carrier[Payload27[T]] + c28*: Carrier[Payload28[T]] + c29*: Carrier[Payload29[T]] + c30*: Carrier[Payload30[T]] + c31*: Carrier[Payload31[T]] + c32*: Carrier[Payload32[T]] + c33*: Carrier[Payload33[T]] + +var carriers : Carriers[int] + +static: + assert $(typeof(carriers.c33.val)) == "Payload33[system.int]" diff --git a/tests/generics/tinheritable_importcpp.nim b/tests/generics/tinheritable_importcpp.nim new file mode 100644 index 000000000000..8ee18b70b21d --- /dev/null +++ b/tests/generics/tinheritable_importcpp.nim @@ -0,0 +1,10 @@ +discard """ + targets: "cpp" + action: compile +""" + +# #4651 +type + Vector[T] {.importcpp: "std::vector<'0 >", header: "vector", inheritable.} = object + VectorDerived {.importcpp: "SomeVectorDerived", nodecl.} = object of Vector[int] + # Error: inheritance only works with non-final objects diff --git a/tests/generics/tparam_binding.nim b/tests/generics/tparam_binding.nim index cd0d58e02f80..fa7558613533 100644 --- a/tests/generics/tparam_binding.nim +++ b/tests/generics/tparam_binding.nim @@ -1,6 +1,7 @@ discard """ + matrix: "--mm:arc; --mm:refc" errormsg: "got " - line: 27 + line: 28 """ type diff --git a/tests/generics/tthread_generic.nim b/tests/generics/tthread_generic.nim index 2af5a761583e..300da56a68c6 100644 --- a/tests/generics/tthread_generic.nim +++ b/tests/generics/tthread_generic.nim @@ -1,5 +1,5 @@ discard """ - cmd: "nim $target --hints:on --threads:on $options $file" + matrix: "--mm:refc; --mm:orc" action: compile """ diff --git a/tests/gensym/tgensymgeneric.nim b/tests/gensym/tgensymgeneric.nim index c17a0715f7c6..6e1df4842fd7 100644 --- a/tests/gensym/tgensymgeneric.nim +++ b/tests/gensym/tgensymgeneric.nim @@ -21,7 +21,7 @@ template genImpl() = gensymed.x = "abc" else: gensymed.x = 123 - shallowCopy(result, gensymed) + result = move gensymed proc gen[T](x: T): T = genImpl() diff --git a/tests/ic/config.nims b/tests/ic/config.nims new file mode 100644 index 000000000000..a522efb0dbfc --- /dev/null +++ b/tests/ic/config.nims @@ -0,0 +1,3 @@ +when defined(windows): + --tlsEmulation:off +--mm:refc diff --git a/tests/iter/titer2.nim b/tests/iter/titer2.nim index f60aed73a0b1..975cc786cf07 100644 --- a/tests/iter/titer2.nim +++ b/tests/iter/titer2.nim @@ -17,7 +17,7 @@ type TSlotEnum = enum seEmpty, seFilled, seDeleted TKeyValuePair[A, B] = tuple[slot: TSlotEnum, key: A, val: B] TKeyValuePairSeq[A, B] = seq[TKeyValuePair[A, B]] - TTable* {.final.}[A, B] = object + TTable*[A, B] {.final.} = object data: TKeyValuePairSeq[A, B] counter: int diff --git a/tests/iter/titer_issues.nim b/tests/iter/titer_issues.nim index 15fe867c8732..9c7a5eab0733 100644 --- a/tests/iter/titer_issues.nim +++ b/tests/iter/titer_issues.nim @@ -29,6 +29,15 @@ end 9018 @[1, 2] @[1, 2, 3] +1 +nested finally +outer finally +nested finally +outer finally +nested finally +outer finally +nested finally +outer finally ''' """ @@ -274,3 +283,82 @@ iterator cc() {.closure.} = break var a2 = cc + +# bug #16876 +block: + iterator a(num: int): int {.closure.} = + if num == 1: + yield num + else: + for i in a(num - 1): + yield i + + for i in a(5): + echo i + +block: + # bug #19911 (return in nested try) + + # try yield -> try + iterator p1: int {.closure.} = + try: + yield 0 + try: + return + finally: + echo "nested finally" + echo "shouldn't run" + finally: + echo "outer finally" + echo "shouldn't run" + + for _ in p1(): + discard + + # try -> try yield + iterator p2: int {.closure.} = + try: + try: + yield 0 + return + finally: + echo "nested finally" + echo "shouldn't run" + finally: + echo "outer finally" + echo "shouldn't run" + + for _ in p2(): + discard + + # try yield -> try yield + iterator p3: int {.closure.} = + try: + yield 0 + try: + yield 0 + return + finally: + echo "nested finally" + echo "shouldn't run" + finally: + echo "outer finally" + echo "shouldn't run" + + for _ in p3(): + discard + + # try -> try + iterator p4: int {.closure.} = + try: + try: + return + finally: + echo "nested finally" + echo "shouldn't run" + finally: + echo "outer finally" + echo "shouldn't run" + + for _ in p4(): + discard diff --git a/tests/iter/tshallowcopy_closures.nim b/tests/iter/tshallowcopy_closures.nim index 279e7d950754..06b04a78801f 100644 --- a/tests/iter/tshallowcopy_closures.nim +++ b/tests/iter/tshallowcopy_closures.nim @@ -1,4 +1,5 @@ discard """ + matrix: "--mm:refc" ccodecheck: "!@('{' \\s* 'NI HEX3Astate;' \\s* '}')" output: ''' a1 10 diff --git a/tests/iter/tyieldintry.nim b/tests/iter/tyieldintry.nim index 9862fe1dec1b..9df201dd4676 100644 --- a/tests/iter/tyieldintry.nim +++ b/tests/iter/tyieldintry.nim @@ -495,3 +495,11 @@ block: #17849 - yield in case subject yield 5 test(it, 1, 2, 13, 5) + +block: # void iterator + iterator it() {.closure.} = + try: + yield + except: + discard + var a = it diff --git a/tests/js/t20233.nim b/tests/js/t20233.nim new file mode 100644 index 000000000000..401d1412293f --- /dev/null +++ b/tests/js/t20233.nim @@ -0,0 +1,7 @@ +discard """ + output: "yes" +""" +case 1.0 +of 1.0..2.0, 4.0: echo "yes" +of 3.0: discard +else: echo "no" \ No newline at end of file diff --git a/tests/js/tindexdefect.nim b/tests/js/tindexdefect.nim new file mode 100644 index 000000000000..37994ec2e749 --- /dev/null +++ b/tests/js/tindexdefect.nim @@ -0,0 +1,9 @@ +discard """ + outputsub: "unhandled exception: index 10000 not in 0 .. 0 [IndexDefect]" + exitcode: 1 + joinable: false +""" + +var s = ['a'] +let z = s[10000] == 'a' +echo z \ No newline at end of file diff --git a/tests/lexer/nim.cfg b/tests/lexer/nim.cfg deleted file mode 100644 index f7a301a10c39..000000000000 --- a/tests/lexer/nim.cfg +++ /dev/null @@ -1 +0,0 @@ ---experimental:unicodeOperators diff --git a/tests/lookups/tredef.nim b/tests/lookups/tredef.nim index 8f1b38bd10ed..7c1df238ecd6 100644 --- a/tests/lookups/tredef.nim +++ b/tests/lookups/tredef.nim @@ -4,7 +4,7 @@ foo(1, "test") proc bar(a: int, b: string) = discard bar(1, "test") -template foo(a: int, b: string) = bar(a, b) +template foo(a: int, b: string) {.redefine.} = bar(a, b) foo(1, "test") block: diff --git a/tests/macros/t19766_20114.nim b/tests/macros/t19766_20114.nim new file mode 100644 index 000000000000..ac336f1504bf --- /dev/null +++ b/tests/macros/t19766_20114.nim @@ -0,0 +1,16 @@ +discard """ + action: compile + nimout: ''' +const + foo {.strdefine.} = "abc" +let hey {.tddd.} = 5 +''' +""" + +import macros + +template tddd {.pragma.} + +expandMacros: + const foo {.strdefine.} = "abc" + let hey {.tddd.} = 5 diff --git a/tests/macros/t20067.nim b/tests/macros/t20067.nim new file mode 100644 index 000000000000..0ee3a471236e --- /dev/null +++ b/tests/macros/t20067.nim @@ -0,0 +1,28 @@ +discard """ + output: ''' +b.defaultVal = foo +$c.defaultVal = bar +''' +""" + +import macros + +# #18976 + +macro getString(identifier): string = + result = newLit($identifier) +doAssert getString(abc) == "abc" +doAssert getString(`a b c`) == "abc" + +# #20067 + +template defaultVal*(value : typed) {.pragma.} + +type A = ref object + b {.defaultVal: "foo".}: string + `$c` {.defaultVal: "bar".}: string + +let a = A(b: "a", `$c`: "b") + +echo "b.defaultVal = " & a.b.getCustomPragmaVal(defaultVal) +echo "$c.defaultVal = " & a.`$c`.getCustomPragmaVal(defaultVal) diff --git a/tests/macros/tgetimpl.nim b/tests/macros/tgetimpl.nim index de132f253899..66722a234400 100644 --- a/tests/macros/tgetimpl.nim +++ b/tests/macros/tgetimpl.nim @@ -1,5 +1,5 @@ discard """ - nimout: '''"muhaha" + nimout: '''foo = "muhaha" proc poo(x, y: int) = let y = x echo ["poo"]''' diff --git a/tests/macros/tgettypeinst.nim b/tests/macros/tgettypeinst.nim index 2f955716045c..e722f14aa9a7 100644 --- a/tests/macros/tgettypeinst.nim +++ b/tests/macros/tgettypeinst.nim @@ -191,14 +191,14 @@ template myAttr3() {.pragma.} template serializationKey(key: string) {.pragma.} type - MyObj = object {.packed,myAttr,serializationKey: "one".} + MyObj {.packed,myAttr,serializationKey: "one".} = object myField {.myAttr2,serializationKey: "two".}: int myField2 {.myAttr3,serializationKey: "three".}: float # field pragmas not currently supported test(MyObj): type - _ = object {.packed,myAttr,serializationKey: "one".} + _ {.packed,myAttr,serializationKey: "one".} = object myField: int myField2: float diff --git a/tests/macros/tstaticparamsmacro.nim b/tests/macros/tstaticparamsmacro.nim index 8bd653920911..2632ca730239 100644 --- a/tests/macros/tstaticparamsmacro.nim +++ b/tests/macros/tstaticparamsmacro.nim @@ -7,9 +7,9 @@ numbers 11 22 AST a -[(11, 22), (33, 44)] +@[(c: 11, d: 22), (c: 33, d: 44)] AST b -([55, 66], [77, 88]) +(e: @[55, 66], f: @[77, 88]) 55 10 20Test diff --git a/tests/macros/tstructuredlogging.nim b/tests/macros/tstructuredlogging.nim index 05bb52a4021a..5e4fdea3f203 100644 --- a/tests/macros/tstructuredlogging.nim +++ b/tests/macros/tstructuredlogging.nim @@ -66,7 +66,7 @@ macro mergeScopes(scopeHolders: typed, newBindings: untyped): untyped = newScopeDefinition.add newAssignment(newIdentNode(k), v) result = quote: - template scopeHolder = `newScopeDefinition` + template scopeHolder {.redefine.} = `newScopeDefinition` template scope(newBindings: untyped) {.dirty.} = mergeScopes(bindSym"scopeHolder", newBindings) diff --git a/tests/macros/tvtable.nim b/tests/macros/tvtable.nim index dc07a726b705..0f322b7d5713 100644 --- a/tests/macros/tvtable.nim +++ b/tests/macros/tvtable.nim @@ -18,7 +18,7 @@ type # it's also possible to use a strongly typed tuple here VTable = array[0..1, pointer] - TBase = object {.inheritable.} + TBase {.inheritable.} = object vtbl: ptr VTable TUserObject1 = object of TBase diff --git a/tests/magics/t10307.nim b/tests/magics/t10307.nim index b5bbfdfa80b5..b5a93c5c6821 100644 --- a/tests/magics/t10307.nim +++ b/tests/magics/t10307.nim @@ -1,5 +1,5 @@ discard """ - cmd: "nim c -d:useGcAssert $file" + cmd: "nim c --mm:refc -d:useGcAssert $file" output: '''running someProc(true) res: yes yes diff --git a/tests/magics/tmagics.nim b/tests/magics/tmagics.nim index 0e412940c9fa..e52912b74863 100644 --- a/tests/magics/tmagics.nim +++ b/tests/magics/tmagics.nim @@ -1,4 +1,5 @@ discard """ + matrix: "--mm:refc" output: ''' true true diff --git a/tests/manyloc/keineschweine/keineschweine.nim.cfg b/tests/manyloc/keineschweine/keineschweine.nim.cfg index ca6c75f6ed33..a670e2b77cc3 100644 --- a/tests/manyloc/keineschweine/keineschweine.nim.cfg +++ b/tests/manyloc/keineschweine/keineschweine.nim.cfg @@ -7,3 +7,4 @@ path = "dependencies/genpacket" path = "enet_server" debugger = off warning[SmallLshouldNotBeUsed] = off +mm = refc diff --git a/tests/manyloc/keineschweine/lib/estreams.nim b/tests/manyloc/keineschweine/lib/estreams.nim index 5ab029b52bb6..c5e45e0e7376 100644 --- a/tests/manyloc/keineschweine/lib/estreams.nim +++ b/tests/manyloc/keineschweine/lib/estreams.nim @@ -78,9 +78,9 @@ proc write*(buffer: PBuffer; val: var string) = setLen buffer.data, buffer.pos + length.int copyMem(addr buffer.data[buffer.pos], addr val[0], length.int) inc buffer.pos, length.int -proc write*[T: SomeNumber|bool|char|byte](buffer: PBuffer; val: T) = +proc write*[T: SomeNumber|bool|char|byte](buffer: PBuffer; val: sink T) = var v: T - shallowCopy v, val + v = val writeBE buffer, v proc readInt8*(buffer: PBuffer): int8 = diff --git a/tests/manyloc/standalone2/tavr.nim.cfg b/tests/manyloc/standalone2/tavr.nim.cfg index e5291969dd3d..2a31618d03df 100644 --- a/tests/manyloc/standalone2/tavr.nim.cfg +++ b/tests/manyloc/standalone2/tavr.nim.cfg @@ -2,3 +2,4 @@ --cpu:avr --os:standalone --compileOnly +--threads:off diff --git a/tests/metatype/tmetatype_various.nim b/tests/metatype/tmetatype_various.nim index 9faeeec4a3e1..c17410a06900 100644 --- a/tests/metatype/tmetatype_various.nim +++ b/tests/metatype/tmetatype_various.nim @@ -1,4 +1,5 @@ discard """ + matrix: "--mm:refc" output: '''[1, 0, 0, 0, 0, 0, 0, 0] CTBool[Ct[system.uint32]]''' """ diff --git a/tests/metatype/tstaticparammacro.nim b/tests/metatype/tstaticparammacro.nim index 16a6e56b80c4..bcf28d331a08 100644 --- a/tests/metatype/tstaticparammacro.nim +++ b/tests/metatype/tstaticparammacro.nim @@ -6,9 +6,9 @@ numbers 11 22 AST a -[(11, 22), (33, 44)] +@[(c: 11, d: 22), (c: 33, d: 44)] AST b -([55, 66], [77, 88]) +(e: @[55, 66], f: @[77, 88]) 55 10 20Test diff --git a/tests/metatype/tstaticvector.nim b/tests/metatype/tstaticvector.nim index ca16518fe8fc..85a66974b5f3 100644 --- a/tests/metatype/tstaticvector.nim +++ b/tests/metatype/tstaticvector.nim @@ -1,9 +1,10 @@ discard """ + matrix: "--mm:orc" output: '''0 0 2 100 -30.0 [data = [2.0]] +30.0 TVec[1, system.float32](data: [2.0]) ''' """ diff --git a/tests/metatype/ttypedesc3.nim b/tests/metatype/ttypedesc3.nim index 98a59f61342e..d3a58853df9f 100644 --- a/tests/metatype/ttypedesc3.nim +++ b/tests/metatype/ttypedesc3.nim @@ -1,5 +1,6 @@ discard """ -output: ''' + matrix: "--mm:arc; --mm:refc" + output: ''' proc Base proc Child method Base diff --git a/tests/method/tgeneric_methods.nim b/tests/method/tgeneric_methods.nim index 0e2aeeedee3e..ab92c66d892d 100644 --- a/tests/method/tgeneric_methods.nim +++ b/tests/method/tgeneric_methods.nim @@ -1,4 +1,5 @@ discard """ + matrix: "--mm:arc; --mm:refc" output: '''wow2 X 1 X 3''' diff --git a/tests/method/tmethod_issues.nim b/tests/method/tmethod_issues.nim index df4c3771af0a..daaa46522742 100644 --- a/tests/method/tmethod_issues.nim +++ b/tests/method/tmethod_issues.nim @@ -1,4 +1,5 @@ discard """ + matrix: "--mm:arc; --mm:refc" output: ''' wof! wof! @@ -9,7 +10,7 @@ type B # bug #1659 -type Animal = ref object {.inheritable.} +type Animal {.inheritable.} = ref object type Dog = ref object of Animal method say(a: Animal): auto {.base.} = "wat!" @@ -159,3 +160,11 @@ proc main() = main() +block: # bug #20391 + type Container[T] = ref object of RootRef + item: T + + let a = Container[int]() + + doAssert a of Container[int] + doAssert not (a of Container[string]) diff --git a/tests/method/tmethod_various.nim b/tests/method/tmethod_various.nim index fd022717bb7d..c41d049837be 100644 --- a/tests/method/tmethod_various.nim +++ b/tests/method/tmethod_various.nim @@ -1,4 +1,5 @@ discard """ + matrix: "--mm:arc; --mm:refc" output: ''' do nothing HELLO WORLD! @@ -11,7 +12,7 @@ method somethin(obj: RootObj) {.base.} = echo "do nothing" type - TNode* = object {.inheritable.} + TNode* {.inheritable.} = object PNode* = ref TNode PNodeFoo* = ref object of TNode @@ -29,7 +30,7 @@ o.somethin() # tmproto type - Obj1 = ref object {.inheritable.} + Obj1 {.inheritable.} = ref object Obj2 = ref object of Obj1 method beta(x: Obj1): int {.base.} diff --git a/tests/method/tmultim.nim b/tests/method/tmultim.nim index b6fed024e7a7..126eb7526bd9 100644 --- a/tests/method/tmultim.nim +++ b/tests/method/tmultim.nim @@ -16,7 +16,7 @@ do nothing # tmultim2 type - TThing = object {.inheritable.} + TThing {.inheritable.} = object TUnit = object of TThing x: int TParticle = object of TThing @@ -49,7 +49,7 @@ staticCollide(a, b) # tmultim6 type - Thing = object {.inheritable.} + Thing {.inheritable.} = object Unit[T] = object of Thing x: T Particle = object of Thing @@ -81,7 +81,7 @@ method somethin(obj: RootObj) {.base.} = echo "do nothing" type - TNode* = object {.inheritable.} + TNode* {.inheritable.} = object PNode* = ref TNode PNodeFoo* = ref object of TNode diff --git a/tests/method/tsingle_methods.nim b/tests/method/tsingle_methods.nim index 40269559a7a1..b564e7d87779 100644 --- a/tests/method/tsingle_methods.nim +++ b/tests/method/tsingle_methods.nim @@ -1,5 +1,5 @@ discard """ - cmd: "nim c --multimethods:off $file" + matrix: "--mm:arc --multimethods:off; --mm:refc --multimethods:off" output: '''base base base diff --git a/tests/misc/m15316.nim b/tests/misc/m15316.nim new file mode 100644 index 000000000000..188d0694637a --- /dev/null +++ b/tests/misc/m15316.nim @@ -0,0 +1 @@ +proc foo = (if) diff --git a/tests/misc/m15955.nim b/tests/misc/m15955.nim new file mode 100644 index 000000000000..22da345db070 --- /dev/null +++ b/tests/misc/m15955.nim @@ -0,0 +1,4 @@ +proc add*(a, b: int): int {.cdecl, exportc.} = + a + b +proc sub*(a, b: int): int {.cdecl, exportc.} = + a - b \ No newline at end of file diff --git a/tests/misc/m15955_main.nim b/tests/misc/m15955_main.nim new file mode 100644 index 000000000000..a71af81219fd --- /dev/null +++ b/tests/misc/m15955_main.nim @@ -0,0 +1,11 @@ +import stdtest/specialpaths +import std/os + +const buildLib = buildDir / "libD20220923T19380" + +{.passL: buildLib.} +proc add*(a, b: int):int {.cdecl, importc.} +proc sub*(a, b: int):int {.cdecl, importc.} + +echo add(10, 5) +echo sub(10, 5) diff --git a/tests/misc/m20149.nim b/tests/misc/m20149.nim new file mode 100644 index 000000000000..942262a6e53f --- /dev/null +++ b/tests/misc/m20149.nim @@ -0,0 +1,2 @@ +let x = 12 +echo x diff --git a/tests/misc/mjsondoc.nim b/tests/misc/mjsondoc.nim new file mode 100644 index 000000000000..e4642f0b490a --- /dev/null +++ b/tests/misc/mjsondoc.nim @@ -0,0 +1,11 @@ +proc doSomething*(x, y: int): int = + ## do something + x + y + +const + a* = 1 ## echo 1234 + b* = "test" + +type + MyEnum* = enum + foo, bar diff --git a/tests/misc/parsecomb.nim b/tests/misc/parsecomb.nim index 4ff2f65d227b..4f149cbf6c44 100644 --- a/tests/misc/parsecomb.nim +++ b/tests/misc/parsecomb.nim @@ -1,3 +1,7 @@ +discard """ + matrix: "--mm:arc; --mm:refc" +""" + type Input[T] = object toks: seq[T] index: int diff --git a/tests/misc/t15955.nim b/tests/misc/t15955.nim new file mode 100644 index 000000000000..7441e5398270 --- /dev/null +++ b/tests/misc/t15955.nim @@ -0,0 +1,22 @@ +discard """ +joinable: false +""" + +import stdtest/specialpaths +import std/[osproc, strformat, os] + +const + nim = getCurrentCompilerExe() + buildLib = buildDir / "libD20220923T19380" + currentDir = splitFile(currentSourcePath).dir + file = currentDir / "m15955.nim" + main = currentDir / "m15955_main.nim" + + +proc runCmd(cmd: string) = + let (msg, code) = execCmdEx(cmd) + doAssert code == 0, msg + + +runCmd fmt"{nim} c -o:{buildLib} --nomain --nimMainPrefix:libA -f --app:staticlib {file}" +runCmd fmt"{nim} c -r {main}" diff --git a/tests/misc/t19046.nim b/tests/misc/t19046.nim new file mode 100644 index 000000000000..b3bfec7aea33 --- /dev/null +++ b/tests/misc/t19046.nim @@ -0,0 +1,19 @@ +discard """ + targets: "c cpp" + matrix: "--threads:on" + disabled: "win" + disabled: "osx" + action: compile +""" + +# bug #19046 + +import std/os + +var t: Thread[void] + +proc test = discard +proc main = + createThread(t, test) + pinToCpu(t, 1) +main() \ No newline at end of file diff --git a/tests/misc/taddr.nim b/tests/misc/taddr.nim index 631c9f265d21..48d4928ac283 100644 --- a/tests/misc/taddr.nim +++ b/tests/misc/taddr.nim @@ -32,6 +32,10 @@ doAssert objDeref.x == 42 # String tests obj.s = "lorem ipsum dolor sit amet" +when defined(gcArc) or defined(gcOrc): + prepareMutation(obj.s) + + var indexAddr = addr(obj.s[2]) doAssert indexAddr[] == 'r' @@ -232,8 +236,17 @@ block: # bug #15939 const bar = proc2(foo) doAssert bar == "foo" +template prepareMutationForOrc(x: string) = + when defined(gcArc) or defined(gcOrc): + when nimvm: + discard + else: + prepareMutation(x) + proc test15939() = # bug #15939 (v2) template fn(a) = + when typeof(a) is string: + prepareMutationForOrc(a) let pa = a[0].addr doAssert pa != nil doAssert pa[] == 'a' @@ -253,6 +266,7 @@ proc test15939() = # bug #15939 (v2) # mycstring[ind].addr template cstringTest = var a2 = "abc" + prepareMutationForOrc(a2) var b2 = a2.cstring fn(b2) when nimvm: cstringTest() diff --git a/tests/usingstmt/tusingstatement.nim b/tests/misc/tcsharpusingstatement.nim similarity index 100% rename from tests/usingstmt/tusingstatement.nim rename to tests/misc/tcsharpusingstatement.nim diff --git a/tests/misc/tdefine.nim b/tests/misc/tdefine.nim index f1c6e7a96dfa..c4d11c941c91 100644 --- a/tests/misc/tdefine.nim +++ b/tests/misc/tdefine.nim @@ -1,6 +1,6 @@ discard """ joinable: false -cmd: "nim c -d:booldef -d:booldef2=false -d:intdef=2 -d:strdef=foobar -r $file" +cmd: "nim c -d:booldef -d:booldef2=false -d:intdef=2 -d:strdef=foobar -d:namespaced.define=false -d:double.namespaced.define -r $file" """ const booldef {.booldefine.} = false @@ -27,4 +27,19 @@ type T = object when intdef2 == 1: field2: int when strdef2 == "abc": - field3: int \ No newline at end of file + field3: int + +doAssert not defined(booldef3) +doAssert not defined(intdef2) +doAssert not defined(strdef2) +discard T(field1: 1, field2: 2, field3: 3) + +doAssert defined(namespaced.define) +const `namespaced.define` {.booldefine.} = true +doAssert not `namespaced.define` + +doAssert defined(double.namespaced.define) +const `double.namespaced.define` {.booldefine.} = false +doAssert `double.namespaced.define` + +doAssert not defined(namespaced.butnotdefined) diff --git a/tests/misc/tevents.nim b/tests/misc/tevents.nim deleted file mode 100644 index 045c9fc5b09e..000000000000 --- a/tests/misc/tevents.nim +++ /dev/null @@ -1,48 +0,0 @@ -discard """ -output: ''' -HandlePrintEvent: Output -> Handled print event -HandlePrintEvent2: Output -> printing for ME -HandlePrintEvent2: Output -> printing for ME -''' -""" - -import events - -type - PrintEventArgs = object of EventArgs - user*: string - -proc handleprintevent*(e: EventArgs) = - write(stdout, "HandlePrintEvent: Output -> Handled print event\n") - -proc handleprintevent2*(e: EventArgs) = - var args: PrintEventArgs = PrintEventArgs(e) - write(stdout, "HandlePrintEvent2: Output -> printing for " & args.user) - -var ee = initEventEmitter() - -var eventargs: PrintEventArgs -eventargs.user = "ME\n" - -##method one test - -ee.on("print", handleprintevent) -ee.on("print", handleprintevent2) - -ee.emit("print", eventargs) - -##method two test - -type - SomeObject = object of RootObj - printEvent: EventHandler - -var obj: SomeObject -obj.printEvent = initEventHandler("print") -obj.printEvent.addHandler(handleprintevent2) - -ee.emit(obj.printEvent, eventargs) - -obj.printEvent.removeHandler(handleprintevent2) - -ee.emit(obj.printEvent, eventargs) diff --git a/tests/misc/tlocals.nim b/tests/misc/tlocals.nim index a6df6822466b..e59976102352 100644 --- a/tests/misc/tlocals.nim +++ b/tests/misc/tlocals.nim @@ -1,4 +1,5 @@ discard """ + matrix: "--mm:refc; --mm:orc" output: '''(x: "string here", a: 1) b is 5 x is 12''' diff --git a/tests/misc/tnew.nim b/tests/misc/tnew.nim index 2d9a6446183d..41ef3fa195e4 100644 --- a/tests/misc/tnew.nim +++ b/tests/misc/tnew.nim @@ -1,4 +1,5 @@ discard """ +matrix: "--mm:refc" outputsub: ''' Simple tree node allocation worked! Simple cycle allocation worked! @@ -10,6 +11,7 @@ joinable: false # and the code generation for gc walkers # (and the garbage collector): +## todo fixme it doesn't work for ORC type PNode = ref TNode TNode = object diff --git a/tests/misc/tradix.nim b/tests/misc/tradix.nim index 1773a96098a4..f4fb568493b1 100644 --- a/tests/misc/tradix.nim +++ b/tests/misc/tradix.nim @@ -40,8 +40,8 @@ type TRadixNode {.pure, inheritable.} = object kind: TRadixNodeKind TRadixNodeLinear = object of TRadixNode - len: int8 - keys: array[0..31, int8] + len: uint8 + keys: array[0..31, uint8] vals: array[0..31, PRadixNode] TRadixNodeFull = object of TRadixNode @@ -49,8 +49,8 @@ type TRadixNodeLeafBits = object of TRadixNode b: array[0..7, int] TRadixNodeLeafLinear = object of TRadixNode - len: int8 - keys: array[0..31, int8] + len: uint8 + keys: array[0..31, uint8] var root: PRadixNode @@ -59,8 +59,8 @@ proc searchInner(r: PRadixNode, a: int): PRadixNode = case r.kind of rnLinear: var x = cast[ptr TRadixNodeLinear](r) - for i in 0..ze(x.len)-1: - if ze(x.keys[i]) == a: return x.vals[i] + for i in 0..int(x.len)-1: + if int(x.keys[i]) == a: return x.vals[i] of rnFull: var x = cast[ptr TRadixNodeFull](r) return x.b[a] @@ -87,8 +87,8 @@ proc searchLeaf(r: PRadixNode, a: int): bool = return testBit(x.b[a /% BitsPerUnit], a) of rnLeafLinear: var x = cast[ptr TRadixNodeLeafLinear](r) - for i in 0..ze(x.len)-1: - if ze(x.keys[i]) == a: return true + for i in 0..int(x.len)-1: + if int(x.keys[i]) == a: return true else: assert(false) proc exclLeaf(r: PRadixNode, a: int) = @@ -98,9 +98,9 @@ proc exclLeaf(r: PRadixNode, a: int) = resetBit(x.b[a /% BitsPerUnit], a) of rnLeafLinear: var x = cast[ptr TRadixNodeLeafLinear](r) - var L = ze(x.len) + var L = int(x.len) for i in 0..L-1: - if ze(x.keys[i]) == a: + if int(x.keys[i]) == a: x.keys[i] = x.keys[L-1] dec(x.len) return @@ -131,8 +131,8 @@ proc addLeaf(r: var PRadixNode, a: int): bool = # a linear node: var x = cast[ptr TRadixNodeLinear](alloc0(sizeof(TRadixNodeLinear))) x.kind = rnLeafLinear - x.len = 1'i8 - x.keys[0] = toU8(a) + x.len = 1'u8 + x.keys[0] = uint8(a) r = x return false # not already in set case r.kind @@ -141,18 +141,18 @@ proc addLeaf(r: var PRadixNode, a: int): bool = return testOrSetBit(x.b[a /% BitsPerUnit], a) of rnLeafLinear: var x = cast[ptr TRadixNodeLeafLinear](r) - var L = ze(x.len) + var L = int(x.len) for i in 0..L-1: - if ze(x.keys[i]) == a: return true + if int(x.keys[i]) == a: return true if L <= high(x.keys): - x.keys[L] = toU8(a) + x.keys[L] = uint8(a) inc(x.len) else: # transform into a full node: var y = cast[ptr TRadixNodeLeafBits](alloc0(sizeof(TRadixNodeLeafBits))) y.kind = rnLeafBits - for i in 0..ze(x.len)-1: - var u = ze(x.keys[i]) + for i in 0..int(x.len)-1: + var u = int(x.keys[i]) setBit(y.b[u /% BitsPerUnit], u) setBit(y.b[a /% BitsPerUnit], a) dealloc(r) @@ -167,26 +167,26 @@ proc addInner(r: var PRadixNode, a: int, d: int): bool = # a linear node: var x = cast[ptr TRadixNodeLinear](alloc0(sizeof(TRadixNodeLinear))) x.kind = rnLinear - x.len = 1'i8 - x.keys[0] = toU8(k) + x.len = 1'u8 + x.keys[0] = uint8(k) r = x return addInner(x.vals[0], a, d-8) case r.kind of rnLinear: var x = cast[ptr TRadixNodeLinear](r) - var L = ze(x.len) + var L = int(x.len) for i in 0..L-1: - if ze(x.keys[i]) == k: # already exists + if int(x.keys[i]) == k: # already exists return addInner(x.vals[i], a, d-8) if L <= high(x.keys): - x.keys[L] = toU8(k) + x.keys[L] = uint8(k) inc(x.len) return addInner(x.vals[L], a, d-8) else: # transform into a full node: var y = cast[ptr TRadixNodeFull](alloc0(sizeof(TRadixNodeFull))) y.kind = rnFull - for i in 0..L-1: y.b[ze(x.keys[i])] = x.vals[i] + for i in 0..L-1: y.b[int(x.keys[i])] = x.vals[i] dealloc(r) r = y return addInner(y.b[k], a, d-8) @@ -211,8 +211,8 @@ iterator innerElements(r: PRadixNode): tuple[prefix: int, n: PRadixNode] = yield (i, r.b[i]) of rnLinear: var r = cast[ptr TRadixNodeLinear](r) - for i in 0..ze(r.len)-1: - yield (ze(r.keys[i]), r.vals[i]) + for i in 0..int(r.len)-1: + yield (int(r.keys[i]), r.vals[i]) else: assert(false) iterator leafElements(r: PRadixNode): int = @@ -228,8 +228,8 @@ iterator leafElements(r: PRadixNode): int = yield i*BitsPerUnit+j of rnLeafLinear: var r = cast[ptr TRadixNodeLeafLinear](r) - for i in 0..ze(r.len)-1: - yield ze(r.keys[i]) + for i in 0..int(r.len)-1: + yield int(r.keys[i]) else: assert(false) iterator elements*(r: PRadixNode): ByteAddress {.inline.} = diff --git a/tests/misc/trunner.nim b/tests/misc/trunner.nim index 5d12c38b6fa9..add92cbfd30d 100644 --- a/tests/misc/trunner.nim +++ b/tests/misc/trunner.nim @@ -216,6 +216,39 @@ sub/mmain.idx""", context let cmd = fmt"{nim} r --backend:{mode} --hints:off --nimcache:{nimcache} {file}" check execCmdEx(cmd) == ("ok3\n", 0) + block: # nim jsondoc # bug #20132 + let file = testsDir / "misc/mjsondoc.nim" + let output = "nimcache_tjsondoc.json" + defer: removeFile(output) + let (msg, exitCode) = execCmdEx(fmt"{nim} jsondoc -o:{output} {file}") + doAssert exitCode == 0, msg + + let data = parseJson(readFile(output))["entries"] + doAssert data.len == 4 + let doSomething = data[0] + doAssert doSomething["name"].getStr == "doSomething" + doAssert doSomething["type"].getStr == "skProc" + doAssert doSomething["line"].getInt == 1 + doAssert doSomething["col"].getInt == 0 + doAssert doSomething["code"].getStr == "proc doSomething(x, y: int): int {.raises: [], tags: [], forbids: [].}" + + block: # nim jsondoc # bug #11953 + let file = testsDir / "misc/mjsondoc.nim" + let destDir = testsDir / "misc/htmldocs" + removeDir(destDir) + defer: removeDir(destDir) + let (msg, exitCode) = execCmdEx(fmt"{nim} jsondoc {file}") + doAssert exitCode == 0, msg + + let data = parseJson(readFile(destDir / "mjsondoc.json"))["entries"] + doAssert data.len == 4 + let doSomething = data[0] + doAssert doSomething["name"].getStr == "doSomething" + doAssert doSomething["type"].getStr == "skProc" + doAssert doSomething["line"].getInt == 1 + doAssert doSomething["col"].getInt == 0 + doAssert doSomething["code"].getStr == "proc doSomething(x, y: int): int {.raises: [], tags: [], forbids: [].}" + block: # further issues with `--backend` let file = testsDir / "misc/mbackend.nim" var cmd = fmt"{nim} doc -b:cpp --hints:off --nimcache:{nimcache} {file}" @@ -232,6 +265,18 @@ sub/mmain.idx""", context let cmd = fmt"{nim} r -b:cpp --hints:off --nimcache:{nimcache} --warningAsError:ProveInit {file}" check execCmdEx(cmd) == ("witness\n", 0) + block: # bug #20149 + let file = testsDir / "misc/m20149.nim" + let cmd = fmt"{nim} r --hints:off --nimcache:{nimcache} --hintAsError:XDeclaredButNotUsed {file}" + check execCmdEx(cmd) == ("12\n", 0) + + block: # bug #15316 + let file = testsDir / "misc/m15316.nim" + let cmd = fmt"{nim} check --hints:off --nimcache:{nimcache} {file}" + check execCmdEx(cmd) == ("m15316.nim(1, 15) Error: expression expected, but found \')\'\nm15316.nim(2, 1) Error: expected: \':\', but got: \'[EOF]\'\nm15316.nim(2, 1) Error: expression expected, but found \'[EOF]\'\nm15316.nim(2, 1) " & + "Error: expected: \')\', but got: \'[EOF]\'\nError: illformed AST: \n", 1) + + block: # config.nims, nim.cfg, hintConf, bug #16557 let cmd = fmt"{nim} r --hint:all:off --hint:conf tests/newconfig/bar/mfoo.nim" let (outp, exitCode) = execCmdEx(cmd, options = {poStdErrToStdOut}) diff --git a/tests/misc/trunner_special.nim b/tests/misc/trunner_special.nim index 47ba44a29678..50a2e4d5adad 100644 --- a/tests/misc/trunner_special.nim +++ b/tests/misc/trunner_special.nim @@ -26,6 +26,6 @@ proc main = block: # SSL nimDisableCertificateValidation integration tests runCmd fmt"{nim} r {options} -d:nimDisableCertificateValidation -d:ssl {testsDir}/untestable/thttpclient_ssl_disabled.nim" block: # SSL certificate check integration tests - runCmd fmt"{nim} r {options} -d:ssl --threads:on {testsDir}/untestable/thttpclient_ssl_remotenetwork.nim" + runCmd fmt"{nim} r {options} -d:ssl --threads:on --mm:refc {testsDir}/untestable/thttpclient_ssl_remotenetwork.nim" main() diff --git a/tests/misc/tsimplesort.nim b/tests/misc/tsimplesort.nim index 4793c63df1f3..395db55e64e8 100644 --- a/tests/misc/tsimplesort.nim +++ b/tests/misc/tsimplesort.nim @@ -10,7 +10,7 @@ type TSlotEnum = enum seEmpty, seFilled, seDeleted TKeyValuePair[A, B] = tuple[slot: TSlotEnum, key: A, val: B] TKeyValuePairSeq[A, B] = seq[TKeyValuePair[A, B]] - TTable* {.final, myShallow.}[A, B] = object + TTable*[A, B] {.final, myShallow.} = object data: TKeyValuePairSeq[A, B] counter: int @@ -137,8 +137,7 @@ proc `$`*[A, B](t: TTable[A, B]): string = # ------------------------------ count tables ------------------------------- type - TCountTable* {.final, myShallow.}[ - A] = object ## table that counts the number of each key + TCountTable*[A] {.final, myShallow.} = object ## table that counts the number of each key data: seq[tuple[key: A, val: int]] counter: int diff --git a/tests/misc/tunsignedconv.nim b/tests/misc/tunsignedconv.nim index 0acb391061fe..28c3f9769238 100644 --- a/tests/misc/tunsignedconv.nim +++ b/tests/misc/tunsignedconv.nim @@ -56,9 +56,13 @@ discard $x0 const x1 = cast[uint](-1) discard $(x1,) -# bug #13698 -let n: csize = 1 # xxx should that be csize_t or is that essential here? -doAssert $n.int32 == "1" +when not defined(nimPreviewSlimSystem): + # bug #13698 + let n: csize = 1 # xxx should that be csize_t or is that essential here? + doAssert $n.int32 == "1" + +let n2: csize_t = 1 +doAssert $n2.int32 == "1" # bug #14616 @@ -66,8 +70,7 @@ let limit = 1'u64 let rangeVar = 0'u64 ..< limit -doAssert repr(rangeVar) == """[a = 0, -b = 0]""" +doAssert repr(rangeVar) == """0 .. 0""", repr(rangeVar) # bug #15210 diff --git a/tests/misc/tupcomingfeatures.nim b/tests/misc/tupcomingfeatures.nim deleted file mode 100644 index d37ce85cffd6..000000000000 --- a/tests/misc/tupcomingfeatures.nim +++ /dev/null @@ -1,35 +0,0 @@ -discard """ - output: '''0 -2 0 -0 -2''' -""" - -{.this: self.} - -type - Foo = object - a, b, x: int - -proc yay(self: Foo) = - echo a, " ", b, " ", x - -proc footest[T](self: var Foo, a: T) = - b = 1+a - yay() - -proc nongeneric(self: Foo) = - echo a, " ", b - -var ff: Foo -footest(ff, -3) -ff.nongeneric - -{.experimental.} -using - c: Foo - x, y: int - -proc usesSig(c) = - echo "yummy" - -proc foobar(c, y) = - echo "yay" diff --git a/tests/misc/tvarnums.nim b/tests/misc/tvarnums.nim index 2aef242e1086..498099c49d7a 100644 --- a/tests/misc/tvarnums.nim +++ b/tests/misc/tvarnums.nim @@ -7,7 +7,7 @@ import strutils type - TBuffer = array[0..10, int8] + TBuffer = array[0..10, uint8] proc toVarNum(x: int32, b: var TBuffer) = # encoding: first bit indicates end of number (0 if at end) @@ -21,11 +21,11 @@ proc toVarNum(x: int32, b: var TBuffer) = # anyway a = abs(x) # first 6 bits: - b[0] = toU8(ord(a >% 63'i32) shl 7 or (ord(x < 0'i32) shl 6) or (int(a) and 63)) + b[0] = uint8(ord(a >% 63'i32) shl 7 or (ord(x < 0'i32) shl 6) or (int(a) and 63)) a = (a shr 6'i32) and 0x03ffffff # skip first 6 bits var i = 1 while a != 0'i32: - b[i] = toU8(ord(a >% 127'i32) shl 7 or (int(a) and 127)) + b[i] = uint8(ord(a >% 127'i32) shl 7 or (int(a) and 127)) inc(i) a = a shr 7'i32 @@ -41,40 +41,40 @@ proc toVarNum64(x: int64, b: var TBuffer) = # anyway a = abs(x) # first 6 bits: - b[0] = toU8(ord(a >% 63'i64) shl 7 or (ord(x < 0'i64) shl 6) or int(a and 63)) + b[0] = uint8(ord(a >% 63'i64) shl 7 or (ord(x < 0'i64) shl 6) or int(a and 63)) a = (a shr 6) and 0x03ffffffffffffff # skip first 6 bits var i = 1 while a != 0'i64: - b[i] = toU8(ord(a >% 127'i64) shl 7 or int(a and 127)) + b[i] = uint8(ord(a >% 127'i64) shl 7 or int(a and 127)) inc(i) a = a shr 7 proc toNum64(b: TBuffer): int64 = # treat first byte different: - result = ze64(b[0]) and 63 + result = int64(b[0]) and 63 var i = 0 Shift = 6'i64 - while (ze(b[i]) and 128) != 0: + while (int(b[i]) and 128) != 0: inc(i) - result = result or ((ze64(b[i]) and 127) shl Shift) + result = result or ((int64(b[i]) and 127) shl Shift) inc(Shift, 7) - if (ze(b[0]) and 64) != 0: # sign bit set? + if (int(b[0]) and 64) != 0: # sign bit set? result = not result +% 1 # this is the same as ``- result`` # but gives no overflow error for low(int) proc toNum(b: TBuffer): int32 = # treat first byte different: - result = int32 ze(b[0]) and 63 + result = int32(b[0]) and 63 var i = 0 Shift = 6'i32 - while (ze(b[i]) and 128) != 0: + while (int(b[i]) and 128) != 0: inc(i) - result = result or ((int32(ze(b[i])) and 127'i32) shl Shift) + result = result or ((int32(b[i]) and 127'i32) shl Shift) Shift = Shift + 7'i32 - if (ze(b[0]) and (1 shl 6)) != 0: # sign bit set? + if (int(b[0]) and (1 shl 6)) != 0: # sign bit set? result = (not result) +% 1'i32 # this is the same as ``- result`` # but gives no overflow error for low(int) diff --git a/tests/modules/a/module_name_clashes.nim b/tests/modules/a/module_name_clashes.nim new file mode 100644 index 000000000000..209526e22107 --- /dev/null +++ b/tests/modules/a/module_name_clashes.nim @@ -0,0 +1,8 @@ +# See `tmodule_name_clashes` + +import ../b/module_name_clashes +type A* = object + b*: B + +proc print*(a: A) = + echo repr a diff --git a/tests/modules/b/module_name_clashes.nim b/tests/modules/b/module_name_clashes.nim new file mode 100644 index 000000000000..6a10cac330ee --- /dev/null +++ b/tests/modules/b/module_name_clashes.nim @@ -0,0 +1,3 @@ +# See `tmodule_name_clashes` + +type B* = object diff --git a/tests/modules/tmodule_name_clashes.nim b/tests/modules/tmodule_name_clashes.nim new file mode 100644 index 000000000000..814d5d1527a9 --- /dev/null +++ b/tests/modules/tmodule_name_clashes.nim @@ -0,0 +1,17 @@ +discard """ +matrix: "--mm:refc" +targets: "c" +ccodecheck: "\\i @('atmaatsmodule_name_clashesdotnim_DatInit000')" +ccodecheck: "\\i @('atmbatsmodule_name_clashesdotnim_DatInit000')" +joinable: false +""" + +# Test module name clashes within same package. +# This was created to test that module symbol mangling functioned correctly +# for the C backend when there are one or more modules with the same name in +# a package, and more than one of them require module initialization procs. +# I'm not sure of the simplest method to cause the init procs to be generated. + +import a/module_name_clashes + +print A() diff --git a/tests/niminaction/Chapter3/various3.nim b/tests/niminaction/Chapter3/various3.nim index 4e028a048dd4..c7cdf7db4470 100644 --- a/tests/niminaction/Chapter3/various3.nim +++ b/tests/niminaction/Chapter3/various3.nim @@ -1,4 +1,5 @@ discard """ +matrix: "--mm:refc" output: ''' Future is no longer empty, 42 ''' diff --git a/tests/niminaction/Chapter7/Tweeter/src/tweeter.nim b/tests/niminaction/Chapter7/Tweeter/src/tweeter.nim index fe39278fb7e6..2fac949b9caa 100644 --- a/tests/niminaction/Chapter7/Tweeter/src/tweeter.nim +++ b/tests/niminaction/Chapter7/Tweeter/src/tweeter.nim @@ -1,5 +1,6 @@ discard """ action: compile +matrix: "--threads:off" """ import asyncdispatch, times diff --git a/tests/objects/t4318.nim b/tests/objects/t4318.nim index 34ff722f5ce1..beadd6909f17 100644 --- a/tests/objects/t4318.nim +++ b/tests/objects/t4318.nim @@ -1,3 +1,8 @@ +discard """ + matrix: "--mm:refc" +""" + + type A = object of RootObj B = object of A diff --git a/tests/objects/tobj_asgn_dont_slice.nim b/tests/objects/tobj_asgn_dont_slice.nim index 2e36b65a3cdd..ce67c4490f2a 100644 --- a/tests/objects/tobj_asgn_dont_slice.nim +++ b/tests/objects/tobj_asgn_dont_slice.nim @@ -1,4 +1,5 @@ discard """ + matrix: "--mm:refc" outputsub: '''ObjectAssignmentDefect''' exitcode: "1" """ diff --git a/tests/objects/tobjconstr.nim b/tests/objects/tobjconstr.nim index 1e4d89d685e1..ee5a5b2213a0 100644 --- a/tests/objects/tobjconstr.nim +++ b/tests/objects/tobjconstr.nim @@ -50,27 +50,30 @@ type BS = object of B C = object of BS z*: int -# inherited fields are ignored, so no fields are set -when true: - var - o: B - o = B(x: 123) - echo o - o = B(y: 678, x: 123) - echo o -# inherited fields are ignored -echo C(x: 128, z: 89) # (y: 0, x: 0) -echo B(y: 678, x: 123) # (y: 678, x: 0) -echo B(x: 123, y: 678) # (y: 678, x: 0) +proc main2 = + # inherited fields are ignored, so no fields are set + when true: + var + o: B + o = B(x: 123) + echo o + o = B(y: 678, x: 123) + echo o -when true: - # correct, both with `var` and `let`; - var b=B(x: 123) - echo b # (y: 0, x: 123) - b=B(y: 678, x: 123) - echo b # (y: 678, x: 123) - b=B(y: b.x, x: b.y) - echo b # (y: 123, x: 678) + # inherited fields are ignored + echo C(x: 128, z: 89) # (y: 0, x: 0) + echo B(y: 678, x: 123) # (y: 678, x: 0) + echo B(x: 123, y: 678) # (y: 678, x: 0) + when true: + # correct, both with `var` and `let`; + var b=B(x: 123) + echo b # (y: 0, x: 123) + b=B(y: 678, x: 123) + echo b # (y: 678, x: 123) + b=B(y: b.x, x: b.y) + echo b # (y: 123, x: 678) + +main2() GC_fullCollect() diff --git a/tests/objects/tobjconstr2.nim b/tests/objects/tobjconstr2.nim index 6253edab00e8..cf4a694b4e44 100644 --- a/tests/objects/tobjconstr2.nim +++ b/tests/objects/tobjconstr2.nim @@ -15,8 +15,8 @@ echo s[0].x # bug #563 type - Foo = - object {.inheritable.} + Foo {.inheritable.} = + object x: int Bar = diff --git a/tests/objects/tobjects_various.nim b/tests/objects/tobjects_various.nim index 315193de98ee..8ec090f42399 100644 --- a/tests/objects/tobjects_various.nim +++ b/tests/objects/tobjects_various.nim @@ -31,7 +31,7 @@ block tobject2: block tofopr: type - TMyType = object {.inheritable.} + TMyType {.inheritable.} = object len: int data: string diff --git a/tests/overload/tselfderef.nim b/tests/overload/tselfderef.nim deleted file mode 100644 index 96f1da42adbc..000000000000 --- a/tests/overload/tselfderef.nim +++ /dev/null @@ -1,20 +0,0 @@ -discard """ -action: compile -""" - -# bug #4671 -{.experimental.} -{.this: self.} -type - SomeObj = object - f: int - -proc f(num: int) = - discard - -var intptr: ptr int -intptr.f() # compiles fine - -proc doSomething(self: var SomeObj) = - var pint: ptr int - pint.f() # Error: expression '.(pint, "f")' cannot be called diff --git a/tests/package/stdlib/stdlib.nimble b/tests/package/stdlib/stdlib.nimble new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/tests/package/stdlib/system.nim b/tests/package/stdlib/system.nim new file mode 100644 index 000000000000..475f8ec5bea8 --- /dev/null +++ b/tests/package/stdlib/system.nim @@ -0,0 +1,2 @@ +# this module is part of tstdlib_name_not_special +doAssert true \ No newline at end of file diff --git a/tests/package/tstdlib_name_not_special.nim b/tests/package/tstdlib_name_not_special.nim new file mode 100644 index 000000000000..e8226a82d96c --- /dev/null +++ b/tests/package/tstdlib_name_not_special.nim @@ -0,0 +1,3 @@ +# Test whether a another package named 'stdlib' can be imported and used. +# This caused a crash in the past. +import stdlib/system \ No newline at end of file diff --git a/tests/parallel/tconvexhull.nim b/tests/parallel/tconvexhull.nim index 0a07e6b76657..a89aa910bc95 100644 --- a/tests/parallel/tconvexhull.nim +++ b/tests/parallel/tconvexhull.nim @@ -1,4 +1,5 @@ discard """ + matrix: "--mm:refc" output: ''' ''' """ diff --git a/tests/parallel/tdeepcopy.nim b/tests/parallel/tdeepcopy.nim index 499ea94d4ed0..96ca15ca3c94 100644 --- a/tests/parallel/tdeepcopy.nim +++ b/tests/parallel/tdeepcopy.nim @@ -1,4 +1,5 @@ discard """ + matrix: "--mm:refc" output: ''' 13 abc called deepCopy for int diff --git a/tests/parallel/tdeepcopy2.nim b/tests/parallel/tdeepcopy2.nim index a9caab604722..e8305173dfbb 100644 --- a/tests/parallel/tdeepcopy2.nim +++ b/tests/parallel/tdeepcopy2.nim @@ -1,4 +1,5 @@ discard """ + matrix: "--mm:refc" output: ''' called deepCopy for int called deepCopy for int diff --git a/tests/parallel/tdisjoint_slice1.nim b/tests/parallel/tdisjoint_slice1.nim index edcc30ece186..6892e738302d 100644 --- a/tests/parallel/tdisjoint_slice1.nim +++ b/tests/parallel/tdisjoint_slice1.nim @@ -1,4 +1,5 @@ discard """ + matrix: "--mm:refc" outputsub: "EVEN 28" """ diff --git a/tests/parallel/tflowvar.nim b/tests/parallel/tflowvar.nim index 9d93bc7c8717..e44b29a8763f 100644 --- a/tests/parallel/tflowvar.nim +++ b/tests/parallel/tflowvar.nim @@ -1,4 +1,5 @@ discard """ + matrix: "--mm:refc" output: '''foobarfoobar bazbearbazbear diff --git a/tests/parallel/tinvalid_array_bounds.nim b/tests/parallel/tinvalid_array_bounds.nim index 15bf526df769..8dc93c33ffbd 100644 --- a/tests/parallel/tinvalid_array_bounds.nim +++ b/tests/parallel/tinvalid_array_bounds.nim @@ -1,6 +1,7 @@ discard """ + matrix: "--mm:refc" errormsg: "cannot prove (i)..(i) disjoint from (i + 1)..(i + 1)" - line: 20 + line: 21 """ import threadpool diff --git a/tests/parallel/tmissing_deepcopy.nim b/tests/parallel/tmissing_deepcopy.nim index 7803439fa7b2..ea77936ada94 100644 --- a/tests/parallel/tmissing_deepcopy.nim +++ b/tests/parallel/tmissing_deepcopy.nim @@ -1,4 +1,5 @@ discard """ + matrix: "--mm:refc" ccodeCheck: "@'genericDeepCopy(' .*" action: compile """ diff --git a/tests/parallel/tnon_disjoint_slice1.nim b/tests/parallel/tnon_disjoint_slice1.nim index 72d008bbdb28..51762187d672 100644 --- a/tests/parallel/tnon_disjoint_slice1.nim +++ b/tests/parallel/tnon_disjoint_slice1.nim @@ -1,6 +1,7 @@ discard """ + matrix: "--mm:refc" errormsg: "cannot prove (i)..(i) disjoint from (i + 1)..(i + 1)" - line: 20 + line: 21 """ import threadpool diff --git a/tests/parallel/tparfind.nim b/tests/parallel/tparfind.nim index 4b3610c6703f..cf1bc93365f3 100644 --- a/tests/parallel/tparfind.nim +++ b/tests/parallel/tparfind.nim @@ -1,4 +1,5 @@ discard """ + matrix: "--mm:refc" output: "500" """ diff --git a/tests/parallel/tpi.nim b/tests/parallel/tpi.nim index 1abed6f23919..cd965d585fda 100644 --- a/tests/parallel/tpi.nim +++ b/tests/parallel/tpi.nim @@ -1,4 +1,5 @@ discard """ + matrix: "--mm:refc" output: '''3.141792613595791 3.141792613595791''' """ diff --git a/tests/parallel/tsendtwice.nim b/tests/parallel/tsendtwice.nim index 03b7fda47b79..6bf5e5ebb4f0 100644 --- a/tests/parallel/tsendtwice.nim +++ b/tests/parallel/tsendtwice.nim @@ -7,7 +7,7 @@ ob2 @[] ob @[] ob3 @[] ''' - cmd: "nim c -r --threads:on $file" + matrix: "--mm:refc" """ # bug #4776 diff --git a/tests/parallel/tsysspawn.nim b/tests/parallel/tsysspawn.nim index 09a77b358acf..b7ecd1264c8b 100644 --- a/tests/parallel/tsysspawn.nim +++ b/tests/parallel/tsysspawn.nim @@ -5,7 +5,7 @@ discard """ 2 2 ''' - cmd: "nim $target --threads:on $options $file" + matrix: "--mm:refc" """ import threadpool diff --git a/tests/parallel/tuseafterdef.nim b/tests/parallel/tuseafterdef.nim index f1ae6e923373..e73f1b7946d0 100644 --- a/tests/parallel/tuseafterdef.nim +++ b/tests/parallel/tuseafterdef.nim @@ -1,6 +1,7 @@ discard """ + matrix: "--mm:refc" errormsg: "(k)..(k) not disjoint from (k)..(k)" - line: 23 + line: 24 action: compile """ diff --git a/tests/parallel/twaitany.nim b/tests/parallel/twaitany.nim index b58cadd86fa9..d57c5f40fc16 100644 --- a/tests/parallel/twaitany.nim +++ b/tests/parallel/twaitany.nim @@ -1,4 +1,5 @@ discard """ + matrix: "--mm:refc" output: '''true''' """ diff --git a/tests/parser/t12274.nim b/tests/parser/t12274.nim index 40c85f158578..6b7c9f55aec6 100644 --- a/tests/parser/t12274.nim +++ b/tests/parser/t12274.nim @@ -1,3 +1,7 @@ +discard """ + joinable: false +""" + var s: seq[int] s.add block: let i = 1 diff --git a/tests/parser/tprecedence.nim b/tests/parser/tprecedence.nim index 66a2922dbd7e..9be79543b7c1 100644 --- a/tests/parser/tprecedence.nim +++ b/tests/parser/tprecedence.nim @@ -54,3 +54,10 @@ let const test = proc(): int = 1 + +# bug #8759 +block: + template `=>`(a, b): untyped = (a, b) + template `+=`(a, b): untyped = a * b + + doAssert ("abc" => 3 += 5) == ("abc", 15) diff --git a/tests/parser/tstmtlists.nim b/tests/parser/tstmtlists.nim index fa47ba36487e..158c93340a43 100644 --- a/tests/parser/tstmtlists.nim +++ b/tests/parser/tstmtlists.nim @@ -158,7 +158,7 @@ template dim2: int = else: int.high) -template dim: int = +template dim3: int = ( if int.high == 0: int.high diff --git a/tests/pragmas/t12640.nim b/tests/pragmas/t12640.nim index 60177d034d8c..c85185699b08 100644 --- a/tests/pragmas/t12640.nim +++ b/tests/pragmas/t12640.nim @@ -1,4 +1,5 @@ discard """ + matrix: "--mm:refc" nimout: '''1 2 3 @@ -10,7 +11,7 @@ discard """ [1, 2, 3]''' """ - +# todo fixme it doesn't work with ORC proc doIt(a: openArray[int]) = echo a diff --git a/tests/pragmas/tcustom_pragma.nim b/tests/pragmas/tcustom_pragma.nim index db25361889dd..5a68b7677b32 100644 --- a/tests/pragmas/tcustom_pragma.nim +++ b/tests/pragmas/tcustom_pragma.nim @@ -399,6 +399,13 @@ block: discard Hello(a: 1.0, b: 12) +# custom pragma on iterators +block: + template prag {.pragma.} + {.push prag.} + proc hello = discard + iterator hello2: int = discard + # issue #11511 when false: template myAttr {.pragma.} @@ -432,3 +439,43 @@ when false: # left-to-right priority/override order for getCustomPragmaVal assert bb.getCustomPragmaVal(hehe) == (key: "hi", val: "hu", haha: "he") + +{.experimental: "dynamicBindSym".} + +# const +block: + template myAttr() {.pragma.} + template myAttr2(x: int) {.pragma.} + template myAttr3(x: string) {.pragma.} + + type + MyObj2 = ref object + + const a {.myAttr,myAttr2(2),myAttr3:"test".}: int = 0 + const b {.myAttr,myAttr2(2),myAttr3:"test".} = 0 + + macro forceHasCustomPragma(x: untyped, y: typed): untyped = + var x = bindSym(x.repr) + for c in x: + if c.symKind == nskConst: + x = c + break + result = getAst(hasCustomPragma(x, y)) + + macro forceGetCustomPragmaVal(x: untyped, y: typed): untyped = + var x = bindSym(x.repr) + for c in x: + if c.symKind == nskConst: + x = c + break + result = getAst(getCustomPragmaVal(x, y)) + + template check(s: untyped) = + doAssert forceHasCustomPragma(s, myAttr) + doAssert forceHasCustomPragma(s, myAttr2) + doAssert forceGetCustomPragmaVal(s, myAttr2) == 2 + doAssert forceHasCustomPragma(s, myAttr3) + doAssert forceGetCustomPragmaVal(s, myAttr3) == "test" + + check(a) + check(b) diff --git a/tests/pragmas/tlocks.nim b/tests/pragmas/tlocks.nim index ba66a2dca258..6c2a9f9e9f54 100644 --- a/tests/pragmas/tlocks.nim +++ b/tests/pragmas/tlocks.nim @@ -1,3 +1,6 @@ +discard """ + matrix: "--mm:arc; --mm:refc" +""" type SomeBase* = ref object of RootObj type SomeDerived* = ref object of SomeBase diff --git a/tests/pragmas/tnoreturn.nim b/tests/pragmas/tnoreturn.nim index 6d0466df3b75..6a58055fe5db 100644 --- a/tests/pragmas/tnoreturn.nim +++ b/tests/pragmas/tnoreturn.nim @@ -1,4 +1,5 @@ discard """ +matrix: "--mm:refc" ccodeCheck: "\\i @'__attribute__((noreturn))' .*" action: compile """ diff --git a/tests/pragmas/tvar_macro.nim b/tests/pragmas/tvar_macro.nim index d6a4ff9836f8..3fb6e3e530fb 100644 --- a/tests/pragmas/tvar_macro.nim +++ b/tests/pragmas/tvar_macro.nim @@ -85,7 +85,7 @@ block: # with other pragmas let importedFooBar {.importc: "exportedFooBar", nodecl.}: set[char] doAssert importedFooBar == fooBar #[tt.Warning - ^ fooBar is deprecated + ^ fooBar is deprecated ]# diff --git a/tests/range/trange.nim b/tests/range/trange.nim index 92ab5ea867a5..abfa7d474ee0 100644 --- a/tests/range/trange.nim +++ b/tests/range/trange.nim @@ -74,37 +74,37 @@ block tn8vsint16: import strutils block tcolors: - type TColor = distinct int32 + type TColor = distinct uint32 proc rgb(r, g, b: range[0..255]): TColor = result = TColor(r or g shl 8 or b shl 16) proc `$`(c: TColor): string = - result = "#" & toHex(int32(c), 6) + result = "#" & toHex(uint32(c), 6) echo rgb(34, 55, 255) - when false: + block: type - TColor = distinct int32 - TColorComponent = distinct int8 + TColor = distinct uint32 + TColorComponent = distinct uint8 proc red(a: TColor): TColorComponent = - result = TColorComponent(int32(a) and 0xff'i32) + result = TColorComponent(uint32(a) and 0xff'u32) proc green(a: TColor): TColorComponent = - result = TColorComponent(int32(a) shr 8'i32 and 0xff'i32) + result = TColorComponent(uint32(a) shr 8'u32 and 0xff'u32) proc blue(a: TColor): TColorComponent = - result = TColorComponent(int32(a) shr 16'i32 and 0xff'i32) + result = TColorComponent(uint32(a) shr 16'u32 and 0xff'u32) proc rgb(r, g, b: range[0..255]): TColor = result = TColor(r or g shl 8 or b shl 8) proc `+!` (a, b: TColorComponent): TColorComponent = ## saturated arithmetic: - result = TColorComponent(min(ze(int8(a)) + ze(int8(b)), 255)) + result = TColorComponent(min(int(uint8(a)) + int(uint8(b)), 255)) proc `+` (a, b: TColor): TColor = ## saturated arithmetic for colors makes sense, I think: - return rgb(red(a) +! red(b), green(a) +! green(b), blue(a) +! blue(b)) + return rgb(int(red(a) +! red(b)), int(green(a) +! green(b)), int(blue(a) +! blue(b))) - rgb(34, 55, 255) + discard rgb(34, 55, 255) block: type diff --git a/tests/readme.md b/tests/readme.md index dca7ec1522be..b67ec55db18d 100644 --- a/tests/readme.md +++ b/tests/readme.md @@ -27,25 +27,6 @@ Options: There are certain spec keys that imply ``run``, including ``output`` and ``outputsub``. -## cmd - -Specifies the Nim command to use for compiling the test. - -There are a number of variables that are replaced in this spec option: - -* ``$target`` - the compilation target, e.g. ``c``. -* ``$options`` - the options for the compiler. -* ``$file`` - the filename of the test. -* ``$filedir`` - the directory of the test file. - -Example: - -```nim -discard """ - cmd: "nim $target --nimblePath:./nimbleDir/simplePkgs $options $file" -""" -``` - # Categories Each folder under this directory represents a test category, which can be diff --git a/tests/sets/tsets.nim b/tests/sets/tsets.nim index 9ccf7a0c2acf..517c0b89cbac 100644 --- a/tests/sets/tsets.nim +++ b/tests/sets/tsets.nim @@ -76,11 +76,11 @@ block: k0, k1, k2, k3, k4, k5, k6, k7, k8, k9, k10, k11, k12, k13, k14, k15, k16, k17, k18, k19, k20, k21, k22, k23, k24, k25, k26, k27, k28, k29, k30, k31, k32, k33, k34, k35, k36, k37, k38, k39, k40, k41, k42, k43, k44, k45, k46, k47, k48, k49, k50, k51, k52, k53, k54, k55, k56, k57, k58, k59, k60, k61, k62, k63, k64, k65, k66, k67, k68, k69, k70, k71, k72, k73, k74, k75, k76, k77, k78, k79, k80, k81, k82, k83, k84, k85, k86, k87, k88, k89, k90, k91, k92, k93, k94, k95, k96, k97, k98, k99, k100 type - FakeMsgKind2 = range[k50..high(FakeMsgKind)] + FakeMsgKind2 = range[FakeMsgKind.k50..high(FakeMsgKind)] FakeMsgKind2s = set[FakeMsgKind2] const - a1: array[0..0, FakeMsgKind2s] = [{low(FakeMsgKind2)..high(FakeMsgKind2)} - {k99}] + a1: array[0..0, FakeMsgKind2s] = [{low(FakeMsgKind2)..high(FakeMsgKind2)} - {FakeMsgKind.k99}] a2 = a1[0] var diff --git a/tests/statictypes/tstaticimportcpp.nim b/tests/statictypes/tstaticimportcpp.nim index 125dcb98d845..ec4696379556 100644 --- a/tests/statictypes/tstaticimportcpp.nim +++ b/tests/statictypes/tstaticimportcpp.nim @@ -23,12 +23,12 @@ struct SimpleStruct { """ .} type - GenericIntType {.importcpp: "GenericIntType<'0, '1>".} [N: static[int]; T] = object + GenericIntType[N: static[int]; T] {.importcpp: "GenericIntType<'0, '1>".} = object data: array[N, T] - GenericIntTypeAlt {.importcpp: "GenericIntType".} [N: static[int]; T] = object + GenericIntTypeAlt[N: static[int]; T] {.importcpp: "GenericIntType".} = object - GenericTType {.importcpp: "GenericTType<'0>".} [T] = object + GenericTType[T] {.importcpp: "GenericTType<'0>".} = object field: T GenInt4 = GenericIntType[4, int] diff --git a/tests/stdlib/concurrency/tatomics.nim b/tests/stdlib/concurrency/tatomics.nim index beae0ed6d160..9cfdce83d085 100644 --- a/tests/stdlib/concurrency/tatomics.nim +++ b/tests/stdlib/concurrency/tatomics.nim @@ -1,6 +1,8 @@ # test atomic operations import std/[atomics, bitops] +import std/assertions + type Object = object diff --git a/tests/stdlib/concurrency/tatomics_size.nim b/tests/stdlib/concurrency/tatomics_size.nim index 49387c0c1d63..7b43787fbef5 100644 --- a/tests/stdlib/concurrency/tatomics_size.nim +++ b/tests/stdlib/concurrency/tatomics_size.nim @@ -2,6 +2,7 @@ discard """ targets: "c cpp" """ import std/atomics +import std/assertions block testSize: # issue 12726 type diff --git a/tests/stdlib/config.nims b/tests/stdlib/config.nims index 0c0c5bd8846d..ea5d738e2bb0 100644 --- a/tests/stdlib/config.nims +++ b/tests/stdlib/config.nims @@ -1,2 +1,3 @@ switch("styleCheck", "usages") -switch("styleCheck", "error") \ No newline at end of file +switch("styleCheck", "error") +switch("define", "nimPreviewSlimSystem") \ No newline at end of file diff --git a/tests/stdlib/mgenast.nim b/tests/stdlib/mgenast.nim index 2b5381891fcb..b0904847ef3b 100644 --- a/tests/stdlib/mgenast.nim +++ b/tests/stdlib/mgenast.nim @@ -31,6 +31,8 @@ macro bindme6UseExpose*(): untyped = genAst: var tst = "sometext" var ss = newStringStream("anothertext") + when defined(gcArc) or defined(gcOrc): + prepareMutation(tst) writeData(ss, tst[0].addr, 2) discard readData(ss, tst[0].addr, 2) @@ -40,6 +42,8 @@ macro bindme6UseExposeFalse*(): untyped = genAstOpt({kDirtyTemplate}, newStringStream, writeData, readData): var tst = "sometext" var ss = newStringStream("anothertext") + when defined(gcArc) or defined(gcOrc): + prepareMutation(tst) writeData(ss, tst[0].addr, 2) discard readData(ss, tst[0].addr, 2) diff --git a/tests/stdlib/mintsets.nim b/tests/stdlib/mintsets.nim index b4d9ed5162ab..98786e9ba5d6 100644 --- a/tests/stdlib/mintsets.nim +++ b/tests/stdlib/mintsets.nim @@ -1,4 +1,5 @@ import std/intsets +import std/assertions proc test1*[]() = let a = initIntSet() diff --git a/tests/stdlib/t10231.nim b/tests/stdlib/t10231.nim index 3b2b684f3c16..3d09721aaed3 100644 --- a/tests/stdlib/t10231.nim +++ b/tests/stdlib/t10231.nim @@ -5,6 +5,7 @@ discard """ """ import os +import std/assertions # consider moving this inside tosproc (taking care that it's for cpp mode) diff --git a/tests/stdlib/t14139.nim b/tests/stdlib/t14139.nim index 07d2ff137645..866bdb45f72e 100644 --- a/tests/stdlib/t14139.nim +++ b/tests/stdlib/t14139.nim @@ -1,4 +1,5 @@ -import heapqueue +import std/heapqueue +import std/assertions var test_queue : HeapQueue[int] diff --git a/tests/stdlib/t7686.nim b/tests/stdlib/t7686.nim index c174dfb51e07..9902cfcb585f 100644 --- a/tests/stdlib/t7686.nim +++ b/tests/stdlib/t7686.nim @@ -1,4 +1,5 @@ -import strutils +import std/strutils +import std/assertions type MyEnum = enum diff --git a/tests/stdlib/t8925.nim b/tests/stdlib/t8925.nim index dbf55fd882b9..c55e93e736b1 100644 --- a/tests/stdlib/t8925.nim +++ b/tests/stdlib/t8925.nim @@ -1,5 +1,5 @@ discard """ - errormsg: "type mismatch between pattern '$i' (position: 1) and HourRange var 'hour'" + errormsg: "type mismatch between pattern '$$i' (position: 1) and HourRange var 'hour'" file: "strscans.nim" """ diff --git a/tests/stdlib/talgorithm.nim b/tests/stdlib/talgorithm.nim index 61d2bc62f98c..83a84f956a41 100644 --- a/tests/stdlib/talgorithm.nim +++ b/tests/stdlib/talgorithm.nim @@ -6,6 +6,7 @@ discard """ #12928,10456 import std/[sequtils, algorithm, json, sugar] +import std/assertions proc test() = try: diff --git a/tests/stdlib/tarithmetics.nim b/tests/stdlib/tarithmetics.nim index 296ccd56ebd3..a69334e71c61 100644 --- a/tests/stdlib/tarithmetics.nim +++ b/tests/stdlib/tarithmetics.nim @@ -1,7 +1,7 @@ discard """ targets: "c cpp js" """ - +import std/assertions # TODO: in future work move existing arithmetic tests (tests/arithm/*) into this file # FYI https://github.com/nim-lang/Nim/pull/17767 diff --git a/tests/stdlib/tasynchttpserver.nim b/tests/stdlib/tasynchttpserver.nim index aed21099d80d..8cf0d0cedbee 100644 --- a/tests/stdlib/tasynchttpserver.nim +++ b/tests/stdlib/tasynchttpserver.nim @@ -7,6 +7,7 @@ discard """ import strutils from net import TimeoutError +import std/assertions import httpclient, asynchttpserver, asyncdispatch, asyncfutures diff --git a/tests/stdlib/tasynchttpserver_transferencoding.nim b/tests/stdlib/tasynchttpserver_transferencoding.nim index cdd30d04b66b..dae87be82510 100644 --- a/tests/stdlib/tasynchttpserver_transferencoding.nim +++ b/tests/stdlib/tasynchttpserver_transferencoding.nim @@ -8,6 +8,7 @@ import net import std/asyncnet import std/nativesockets +import std/assertions const postBegin = """ POST / HTTP/1.1 diff --git a/tests/stdlib/tbase64.nim b/tests/stdlib/tbase64.nim index 8ef5adf57674..60fa3865d66b 100644 --- a/tests/stdlib/tbase64.nim +++ b/tests/stdlib/tbase64.nim @@ -1,7 +1,7 @@ discard """ targets: "c js" """ - +import std/assertions import std/base64 template main() = diff --git a/tests/stdlib/tbitops.nim b/tests/stdlib/tbitops.nim index f807993d6437..c90943a93c96 100644 --- a/tests/stdlib/tbitops.nim +++ b/tests/stdlib/tbitops.nim @@ -5,6 +5,7 @@ OK ''' """ import bitops +import std/assertions proc main() = const U8 = 0b0011_0010'u8 diff --git a/tests/stdlib/tbitops_utils.nim b/tests/stdlib/tbitops_utils.nim index b571baeaeafe..7a64ea68db3b 100644 --- a/tests/stdlib/tbitops_utils.nim +++ b/tests/stdlib/tbitops_utils.nim @@ -1,4 +1,5 @@ import std/private/bitops_utils +import std/assertions template chk(a, b) = let a2 = castToUnsigned(a) diff --git a/tests/stdlib/tcgi.nim b/tests/stdlib/tcgi.nim index 993728712151..7a52dc89b687 100644 --- a/tests/stdlib/tcgi.nim +++ b/tests/stdlib/tcgi.nim @@ -1,5 +1,6 @@ import std/unittest import std/[cgi, strtabs, sugar] +import std/assertions block: # Test cgi module const queryString = "foo=bar&фу=бар&checked=✓&list=1,2,3&with_space=text%20with%20space" diff --git a/tests/stdlib/tcmdline.nim b/tests/stdlib/tcmdline.nim index bc78d605762e..5c0f717724cc 100644 --- a/tests/stdlib/tcmdline.nim +++ b/tests/stdlib/tcmdline.nim @@ -4,6 +4,7 @@ discard """ """ import std/os +import std/assertions var params = paramCount() doAssert params == 0 diff --git a/tests/stdlib/tcomplex.nim b/tests/stdlib/tcomplex.nim index 15267b90518a..c7666be84e2c 100644 --- a/tests/stdlib/tcomplex.nim +++ b/tests/stdlib/tcomplex.nim @@ -1,5 +1,5 @@ import std/[complex, math] - +import std/assertions proc `=~`[T](x, y: Complex[T]): bool = result = abs(x.re-y.re) < 1e-6 and abs(x.im-y.im) < 1e-6 diff --git a/tests/stdlib/tcookies.nim b/tests/stdlib/tcookies.nim index 0a36cbebcd74..4fe104dfc2c7 100644 --- a/tests/stdlib/tcookies.nim +++ b/tests/stdlib/tcookies.nim @@ -4,6 +4,7 @@ discard """ import std/[cookies, times, strtabs] +import std/assertions let expire = fromUnix(0) + 1.seconds diff --git a/tests/stdlib/tcritbits.nim b/tests/stdlib/tcritbits.nim index b350cb280671..0c2e1d6fad35 100644 --- a/tests/stdlib/tcritbits.nim +++ b/tests/stdlib/tcritbits.nim @@ -3,6 +3,7 @@ discard """ """ import std/[sequtils,critbits] +import std/assertions template main = var r: CritBitTree[void] diff --git a/tests/stdlib/tcstring.nim b/tests/stdlib/tcstring.nim index 04a26b53cb51..d7fdd7738331 100644 --- a/tests/stdlib/tcstring.nim +++ b/tests/stdlib/tcstring.nim @@ -5,6 +5,7 @@ discard """ from std/sugar import collect from stdtest/testutils import whenRuntimeJs, whenVMorJs +import std/assertions template testMitems() = block: diff --git a/tests/stdlib/tcstrutils.nim b/tests/stdlib/tcstrutils.nim index ba3b1de6844b..ec2b8596cacc 100644 --- a/tests/stdlib/tcstrutils.nim +++ b/tests/stdlib/tcstrutils.nim @@ -3,7 +3,7 @@ discard """ """ import std/cstrutils - +import std/assertions proc main() = let s = cstring "abcdef" diff --git a/tests/stdlib/tdb.nim b/tests/stdlib/tdb.nim new file mode 100644 index 000000000000..dbb0283bf3f9 --- /dev/null +++ b/tests/stdlib/tdb.nim @@ -0,0 +1,25 @@ +discard """ + action: "compile" +""" + + +import db_mysql, db_odbc, db_postgres +import os +from stdtest/specialpaths import buildDir + + +block: + block: + const dbName = buildDir / "db.sqlite3" + var db = db_mysql.open(dbName, "", "", "") + discard tryInsertId(db, sql"INSERT INTO myTestTbl (name,i,f) VALUES (?,?,?)", "t") + + block: + const dbName = buildDir / "db.odbc" + var db = db_odbc.open(dbName, "", "", "") + discard tryInsertId(db, sql"INSERT INTO myTestTbl (name,i,f) VALUES (?,?,?)", "t") + + block: + const dbName = buildDir / "db.postgres" + var db = db_postgres.open(dbName, "", "", "") + discard tryInsertId(db, sql"INSERT INTO myTestTbl (name,i,f) VALUES (?,?,?)", "t") diff --git a/tests/stdlib/tdb.nims b/tests/stdlib/tdb.nims new file mode 100644 index 000000000000..d31d0b26fd15 --- /dev/null +++ b/tests/stdlib/tdb.nims @@ -0,0 +1 @@ +--styleCheck:off \ No newline at end of file diff --git a/tests/stdlib/tdb_mysql.nim b/tests/stdlib/tdb_mysql.nim new file mode 100644 index 000000000000..d97358e9f769 --- /dev/null +++ b/tests/stdlib/tdb_mysql.nim @@ -0,0 +1,5 @@ +import std/db_mysql +import std/assertions + +doAssert dbQuote("SELECT * FROM foo WHERE col1 = 'bar_baz'") == "'SELECT * FROM foo WHERE col1 = \\'bar_baz\\''" +doAssert dbQuote("SELECT * FROM foo WHERE col1 LIKE '%bar_baz%'") == "'SELECT * FROM foo WHERE col1 LIKE \\'%bar_baz%\\''" diff --git a/tests/stdlib/tdecls.nim b/tests/stdlib/tdecls.nim index 4e7407045cb5..5cf352cfbfc4 100644 --- a/tests/stdlib/tdecls.nim +++ b/tests/stdlib/tdecls.nim @@ -1,7 +1,7 @@ discard """ targets: "c cpp js" """ - +import std/assertions import std/decls template fun() = diff --git a/tests/stdlib/tdecode_helpers.nim b/tests/stdlib/tdecode_helpers.nim index 626a014fc098..1c0735e05042 100644 --- a/tests/stdlib/tdecode_helpers.nim +++ b/tests/stdlib/tdecode_helpers.nim @@ -1,5 +1,5 @@ import std/private/decode_helpers - +import std/assertions block: var i = 0 diff --git a/tests/stdlib/tdeques.nim b/tests/stdlib/tdeques.nim index 03e951fce476..8a788d337bfb 100644 --- a/tests/stdlib/tdeques.nim +++ b/tests/stdlib/tdeques.nim @@ -5,7 +5,7 @@ discard """ import std/deques from std/sequtils import toSeq - +import std/assertions block: proc index(self: Deque[int], idx: Natural): int = diff --git a/tests/stdlib/tdiff.nim b/tests/stdlib/tdiff.nim index 694ac619818f..cb9cebb3a838 100644 --- a/tests/stdlib/tdiff.nim +++ b/tests/stdlib/tdiff.nim @@ -4,6 +4,7 @@ discard """ import experimental/diff import std/strutils +import std/assertions proc testHelper(f: seq[Item]): string = for it in f: diff --git a/tests/stdlib/tdochelpers.nim b/tests/stdlib/tdochelpers.nim index 8dcb158cacd1..15d53889133e 100644 --- a/tests/stdlib/tdochelpers.nim +++ b/tests/stdlib/tdochelpers.nim @@ -9,75 +9,103 @@ discard """ import ../../lib/packages/docutils/[rstast, rst, dochelpers] import unittest +import std/assertions -proc rstParseTest(text: string): PRstNode = - proc testMsgHandler(filename: string, line, col: int, msgkind: MsgKind, - arg: string) = - doAssert msgkind == mwBrokenLink +proc testMsgHandler(filename: string, line, col: int, msgkind: MsgKind, + arg: string) = + doAssert msgkind == mwBrokenLink + +proc fromRst(text: string): LangSymbol = + let r = rstParse(text, "-input-", LineRstInit, ColRstInit, + {roNimFile}, + msgHandler=testMsgHandler) + assert r.node.kind == rnRstRef + result = toLangSymbol(r.node) + +proc fromMd(text: string): LangSymbol = let r = rstParse(text, "-input-", LineRstInit, ColRstInit, {roPreferMarkdown, roSupportMarkdown, roNimFile}, msgHandler=testMsgHandler) - result = r.node + assert r.node.kind == rnPandocRef + assert r.node.len == 2 + # this son is the target: + assert r.node.sons[1].kind == rnInner + result = toLangSymbol(r.node.sons[1]) suite "Integration with Nim": test "simple symbol parsing (shortest form)": - let input1 = "g_".rstParseTest - check input1.toLangSymbol == LangSymbol(symKind: "", name: "g") + let expected = LangSymbol(symKind: "", name: "g") + check "g_".fromRst == expected + check "[g]".fromMd == expected + # test also alternative syntax variants of Pandoc Markdown: + check "[g][]".fromMd == expected + check "[this symbol][g]".fromMd == expected test "simple symbol parsing (group of words)": - let input1 = "`Y`_".rstParseTest - check input1.toLangSymbol == LangSymbol(symKind: "", name: "Y") + #let input1 = "`Y`_".rstParseTest + let expected1 = LangSymbol(symKind: "", name: "Y") + check "`Y`_".fromRst == expected1 + check "[Y]".fromMd == expected1 # this means not a statement 'type', it's a backticked identifier `type`: - let input2 = "`type`_".rstParseTest - check input2.toLangSymbol == LangSymbol(symKind: "", name: "type") + let expected2 = LangSymbol(symKind: "", name: "type") + check "`type`_".fromRst == expected2 + check "[type]".fromMd == expected2 - let input3 = "`[]`_".rstParseTest - check input3.toLangSymbol == LangSymbol(symKind: "", name: "[]") + let expected3 = LangSymbol(symKind: "", name: "[]") + check "`[]`_".fromRst == expected3 + # Markdown syntax for this case is NOT [[]] + check "[`[]`]".fromMd == expected3 - let input4 = "`X Y Z`_".rstParseTest - check input4.toLangSymbol == LangSymbol(symKind: "", name: "Xyz") + let expected4 = LangSymbol(symKind: "", name: "Xyz") + check "`X Y Z`_".fromRst == expected4 + check "[X Y Z]".fromMd == expected4 test "simple proc parsing": - let input1 = "proc f".rstParseTest - check input1.toLangSymbol == LangSymbol(symKind: "proc", name: "f") + let expected = LangSymbol(symKind: "proc", name: "f") + check "`proc f`_".fromRst == expected + check "[proc f]".fromMd == expected test "another backticked name": - let input1 = """`template \`type\``_""".rstParseTest - check input1.toLangSymbol == LangSymbol(symKind: "template", name: "type") + let expected = LangSymbol(symKind: "template", name: "type") + check """`template \`type\``_""".fromRst == expected + # no backslash in Markdown: + check """[template `type`]""".fromMd == expected test "simple proc parsing with parameters": - let input1 = "`proc f*()`_".rstParseTest - let input2 = "`proc f()`_".rstParseTest let expected = LangSymbol(symKind: "proc", name: "f", parametersProvided: true) - check input1.toLangSymbol == expected - check input2.toLangSymbol == expected + check "`proc f*()`_".fromRst == expected + check "`proc f()`_".fromRst == expected + check "[proc f*()]".fromMd == expected + check "[proc f()]".fromMd == expected test "symbol parsing with 1 parameter": - let input = "`f(G[int])`_".rstParseTest let expected = LangSymbol(symKind: "", name: "f", parameters: @[("G[int]", "")], parametersProvided: true) - check input.toLangSymbol == expected + check "`f(G[int])`_".fromRst == expected + check "[f(G[int])]".fromMd == expected test "more proc parsing": - let input1 = "`proc f[T](x:G[T]):M[T]`_".rstParseTest - let input2 = "`proc f[ T ] ( x: G [T] ): M[T]`_".rstParseTest - let input3 = "`proc f*[T](x: G[T]): M[T]`_".rstParseTest + let input1 = "`proc f[T](x:G[T]):M[T]`_".fromRst + let input2 = "`proc f[ T ] ( x: G [T] ): M[T]`_".fromRst + let input3 = "`proc f*[T](x: G[T]): M[T]`_".fromRst let expected = LangSymbol(symKind: "proc", name: "f", generics: "[T]", parameters: @[("x", "G[T]")], parametersProvided: true, outType: "M[T]") - check(input1.toLangSymbol == expected) - check(input2.toLangSymbol == expected) - check(input3.toLangSymbol == expected) + check(input1 == expected) + check(input2 == expected) + check(input3 == expected) test "advanced proc parsing with Nim identifier normalization": - let input = """`proc binarySearch*[T, K](a: openarray[T]; key: K; - cmp: proc (x: T; y: K): int)`_""".rstParseTest + let inputRst = """`proc binarySearch*[T, K](a: openarray[T]; key: K; + cmp: proc (x: T; y: K): int)`_""" + let inputMd = """[proc binarySearch*[T, K](a: openarray[T]; key: K; + cmp: proc (x: T; y: K): int)]""" let expected = LangSymbol(symKind: "proc", name: "binarysearch", generics: "[T,K]", @@ -87,11 +115,12 @@ suite "Integration with Nim": ("cmp", "proc(x:T;y:K):int")], parametersProvided: true, outType: "") - check(input.toLangSymbol == expected) + check(inputRst.fromRst == expected) + check(inputMd.fromMd == expected) test "the same without proc": let input = """`binarySearch*[T, K](a: openarray[T]; key: K; - cmp: proc (x: T; y: K): int {.closure.})`_""".rstParseTest + cmp: proc (x: T; y: K): int {.closure.})`_""" let expected = LangSymbol(symKind: "", name: "binarysearch", generics: "[T,K]", @@ -101,27 +130,32 @@ suite "Integration with Nim": ("cmp", "proc(x:T;y:K):int")], parametersProvided: true, outType: "") - check(input.toLangSymbol == expected) + check(input.fromRst == expected) + let inputMd = """[binarySearch*[T, K](a: openarray[T]; key: K; + cmp: proc (x: T; y: K): int {.closure.})]""" + check(inputMd.fromMd == expected) test "operator $ with and without backticks": - let input1 = """`func \`$\`*[T](a: \`open Array\`[T]): string`_""". - rstParseTest - let input2 = """`func $*[T](a: \`open Array\`[T]): string`_""". - rstParseTest + let input1 = """`func \`$\`*[T](a: \`open Array\`[T]): string`_""" + let input1md = "[func `$`*[T](a: `open Array`[T]): string]" + let input2 = """`func $*[T](a: \`open Array\`[T]): string`_""" + let input2md = "[func $*[T](a: `open Array`[T]): string]" let expected = LangSymbol(symKind: "func", name: "$", generics: "[T]", parameters: @[("a", "openarray[T]")], parametersProvided: true, outType: "string") - check(input1.toLangSymbol == expected) - check(input2.toLangSymbol == expected) + check input1.fromRst == expected + check input2.fromRst == expected + check input1md.fromMd == expected + check input2md.fromMd == expected test "operator [] with and without backticks": - let input1 = """`func \`[]\`[T](a: \`open Array\`[T], idx: int): T`_""". - rstParseTest - let input2 = """`func [][T](a: \`open Array\`[T], idx: int): T`_""". - rstParseTest + let input1 = """`func \`[]\`[T](a: \`open Array\`[T], idx: int): T`_""" + let input1md = "[func `[]`[T](a: `open Array`[T], idx: int): T]" + let input2 = """`func [][T](a: \`open Array\`[T], idx: int): T`_""" + let input2md = "[func [][T](a: `open Array`[T], idx: int): T]" let expected = LangSymbol(symKind: "func", name: "[]", generics: "[T]", @@ -129,21 +163,25 @@ suite "Integration with Nim": ("idx", "int")], parametersProvided: true, outType: "T") - check(input1.toLangSymbol == expected) - check(input2.toLangSymbol == expected) + check input1.fromRst == expected + check input2.fromRst == expected + check input1md.fromMd == expected + check input2md.fromMd == expected test "postfix symbol specifier #1": - let input = """`walkDir iterator`_""". - rstParseTest + let input = "`walkDir iterator`_" + let inputMd = "[walkDir iterator]" let expected = LangSymbol(symKind: "iterator", name: "walkdir") - check(input.toLangSymbol == expected) + check input.fromRst == expected + check inputMd.fromMd == expected test "postfix symbol specifier #2": - let input1 = """`\`[]\`[T](a: \`open Array\`[T], idx: int): T func`_""". - rstParseTest - let input2 = """`[][T](a: \`open Array\`[T], idx: int): T func`_""". - rstParseTest + let input1 = """`\`[]\`[T](a: \`open Array\`[T], idx: int): T func`_""" + let input1md = "[`[]`[T](a: `open Array`[T], idx: int): T func]" + let input2 = """`[][T](a: \`open Array\`[T], idx: int): T func`_""" + # note again that ` is needed between 1st and second [ + let input2md = "[`[]`[T](a: `open Array`[T], idx: int): T func]" let expected = LangSymbol(symKind: "func", name: "[]", generics: "[T]", @@ -151,11 +189,16 @@ suite "Integration with Nim": ("idx", "int")], parametersProvided: true, outType: "T") - check(input1.toLangSymbol == expected) - check(input2.toLangSymbol == expected) + check input1.fromRst == expected + check input2.fromRst == expected + check input1md.fromMd == expected + check input2md.fromMd == expected test "type of type": - check ("`CopyFlag enum`_".rstParseTest.toLangSymbol == - LangSymbol(symKind: "type", - symTypeKind: "enum", - name: "Copyflag")) + let inputRst = "`CopyFlag enum`_" + let inputMd = "[CopyFlag enum]" + let expected = LangSymbol(symKind: "type", + symTypeKind: "enum", + name: "Copyflag") + check inputRst.fromRst == expected + check inputMd.fromMd == expected diff --git a/tests/stdlib/teditdistance.nim b/tests/stdlib/teditdistance.nim index 4335356356e5..b3b323647b42 100644 --- a/tests/stdlib/teditdistance.nim +++ b/tests/stdlib/teditdistance.nim @@ -1,4 +1,5 @@ import std/editdistance +import std/assertions doAssert editDistance("", "") == 0 doAssert editDistance("kitten", "sitting") == 3 # from Wikipedia diff --git a/tests/stdlib/tencodings.nim b/tests/stdlib/tencodings.nim index 8ca55dbd9d9f..10d79f5d081c 100644 --- a/tests/stdlib/tencodings.nim +++ b/tests/stdlib/tencodings.nim @@ -1,4 +1,5 @@ import std/encodings +import std/assertions var fromGBK = open("utf-8", "gbk") var toGBK = open("gbk", "utf-8") diff --git a/tests/stdlib/tenumerate.nim b/tests/stdlib/tenumerate.nim index 7a1c2d10a1c8..b15b9e2db245 100644 --- a/tests/stdlib/tenumerate.nim +++ b/tests/stdlib/tenumerate.nim @@ -1,4 +1,5 @@ import std/enumerate +import std/assertions let a = @[1, 3, 5, 7] diff --git a/tests/stdlib/tenumutils.nim b/tests/stdlib/tenumutils.nim index 11142216c422..63c563739843 100644 --- a/tests/stdlib/tenumutils.nim +++ b/tests/stdlib/tenumutils.nim @@ -4,6 +4,7 @@ discard """ import std/enumutils from std/sequtils import toSeq +import std/assertions template main = block: # items diff --git a/tests/stdlib/tenvvars.nim b/tests/stdlib/tenvvars.nim index 406aa3230b78..47c1ad24aba8 100644 --- a/tests/stdlib/tenvvars.nim +++ b/tests/stdlib/tenvvars.nim @@ -7,10 +7,14 @@ discard """ import std/envvars from std/sequtils import toSeq import stdtest/testutils +import std/assertions + +# "LATIN CAPITAL LETTER AE" in UTF-8 (0xc386) +const unicodeUtf8 = "\xc3\x86" template main = block: # delEnv, existsEnv, getEnv, envPairs - for val in ["val", ""]: # ensures empty val works too + for val in ["val", "", unicodeUtf8]: # ensures empty val works too const key = "NIM_TESTS_TOSENV_KEY" doAssert not existsEnv(key) @@ -44,9 +48,12 @@ template main = main() +when defined(windows): + proc c_wgetenv(env: WideCString): WideCString {.importc: "_wgetenv", header: "".} +proc c_getenv(env: cstring): cstring {.importc: "getenv", header: "".} + when not defined(js) and not defined(nimscript): block: # bug #18533 - proc c_getenv(env: cstring): cstring {.importc: "getenv", header: "".} var thr: Thread[void] proc threadFunc {.thread.} = putEnv("foo", "fooVal2") @@ -54,6 +61,97 @@ when not defined(js) and not defined(nimscript): doAssert getEnv("foo") == "fooVal1" createThread(thr, threadFunc) joinThreads(thr) - doAssert getEnv("foo") == $c_getenv("foo") + when defined(windows): + doAssert getEnv("foo") == $c_wgetenv("foo".newWideCString) + else: + doAssert getEnv("foo") == $c_getenv("foo".cstring) doAssertRaises(OSError): delEnv("foo=bar") + +when defined(windows): + import std/encodings + + proc c_putenv(env: cstring): int32 {.importc: "putenv", header: "".} + proc c_wputenv(env: WideCString): int32 {.importc: "_wputenv", header: "".} + + block: # Bug #20083 + # These test that `getEnv`, `putEnv` and `existsEnv` handle Unicode + # characters correctly. This means that module X in the process calling the + # CRT environment variable API will get the correct string. Raw CRT API + # calls below represent module X. + + # Getting an env. var. with unicode characters returns the correct UTF-8 + # encoded string. + block: + const envName = "twin_envvars1" + doAssert c_wputenv(newWideCString(envName & "=" & unicodeUtf8)) == 0 + doAssert existsEnv(envName) + doAssert getEnv(envName) == unicodeUtf8 + + # Putting an env. var. with unicode characters gives the correct UTF-16 + # encoded string from low-level routine. + block: + const envName = "twin_envvars2" + putEnv(envName, unicodeUtf8) + doAssert $c_wgetenv(envName.newWideCString) == unicodeUtf8 + + # Env. name containing Unicode characters is retrieved correctly + block: + const envName = unicodeUtf8 & "1" + doAssert c_wputenv(newWideCString(envName & "=" & unicodeUtf8)) == 0 + doAssert existsEnv(envName) + doAssert getEnv(envName) == unicodeUtf8 + + # Env. name containing Unicode characters is set correctly + block: + const envName = unicodeUtf8 & "2" + putEnv(envName, unicodeUtf8) + doAssert existsEnv(envName) + doAssert $c_wgetenv(envName.newWideCString) == unicodeUtf8 + + # Env. name containing Unicode characters and empty value is set correctly + block: + const envName = unicodeUtf8 & "3" + putEnv(envName, "") + doAssert existsEnv(envName) + doAssert $c_wgetenv(envName.newWideCString) == "" + + # It's hard to test on Windows code pages, because there is no "change + # a process' locale" API. + if getCurrentEncoding(true) == "windows-1252": + const + unicodeAnsi = "\xc6" # `unicodeUtf8` in `windows-1252` encoding + + # Test that env. var. ANSI API has correct encoding + block: + const + envName = unicodeUtf8 & "4" + envNameAnsi = unicodeAnsi & "4" + putEnv(envName, unicodeUtf8) + doAssert $c_getenv(envNameAnsi.cstring) == unicodeAnsi + + block: + const + envName = unicodeUtf8 & "5" + envNameAnsi = unicodeAnsi & "5" + doAssert c_putenv((envNameAnsi & "=" & unicodeAnsi).cstring) == 0 + doAssert getEnv(envName) == unicodeUtf8 + + # Env. name containing Unicode characters and empty value is set correctly; + # and, if env. name. characters cannot be represented in codepage, don't + # raise an error. + # + # `win_setenv.nim` converts UTF-16 to ANSI when setting empty env. var. The + # windows-1250 locale has no representation of `abreveUtf8` below, so the + # conversion will fail, but this must not be fatal. It is expected that the + # routine ignores updating MBCS environment (`environ` global) and carries + # on. + block: + const + # "LATIN SMALL LETTER A WITH BREVE" in UTF-8 + abreveUtf8 = "\xc4\x83" + envName = abreveUtf8 & "6" + putEnv(envName, "") + doAssert existsEnv(envName) + doAssert $c_wgetenv(envName.newWideCString) == "" + doAssert getEnv(envName) == "" diff --git a/tests/stdlib/texperimental.nim b/tests/stdlib/texperimental.nim deleted file mode 100644 index ba8c4eb80d73..000000000000 --- a/tests/stdlib/texperimental.nim +++ /dev/null @@ -1 +0,0 @@ -doAssert defined(nimPreviewDotLikeOps) \ No newline at end of file diff --git a/tests/stdlib/tfdleak.nim b/tests/stdlib/tfdleak.nim index 79d7ee0d08ee..1ac746e488fb 100644 --- a/tests/stdlib/tfdleak.nim +++ b/tests/stdlib/tfdleak.nim @@ -7,6 +7,9 @@ discard """ import os, osproc, strutils, nativesockets, net, selectors, memfiles, asyncdispatch, asyncnet + +import std/[assertions, syncio] + when defined(windows): import winlean @@ -56,7 +59,7 @@ proc isValidHandle(f: int): bool = proc main() = if paramCount() == 0: # Parent process - let f = system.open("__test_fdleak", fmReadWrite) + let f = syncio.open("__test_fdleak", fmReadWrite) defer: close f leakCheck(f.getOsFileHandle, "system.open()") diff --git a/tests/stdlib/tfdleak_multiple.nim b/tests/stdlib/tfdleak_multiple.nim index 22387607fa1c..c26681217980 100644 --- a/tests/stdlib/tfdleak_multiple.nim +++ b/tests/stdlib/tfdleak_multiple.nim @@ -3,6 +3,7 @@ joinable: false """ import os, osproc, strutils +import std/assertions const Iterations = 200 diff --git a/tests/stdlib/tfenv.nim b/tests/stdlib/tfenv.nim index 5bcd1ea7c0c3..a486b8a9da38 100644 --- a/tests/stdlib/tfenv.nim +++ b/tests/stdlib/tfenv.nim @@ -1,4 +1,5 @@ import std/fenv +import std/assertions func is_significant(x: float): bool = diff --git a/tests/stdlib/tfrexp1.nim b/tests/stdlib/tfrexp1.nim index 85110231d97f..6b4c3b6d3ec3 100644 --- a/tests/stdlib/tfrexp1.nim +++ b/tests/stdlib/tfrexp1.nim @@ -3,6 +3,7 @@ discard """ """ import std/math +import std/assertions const manualTest = false diff --git a/tests/stdlib/tgenast.nim b/tests/stdlib/tgenast.nim index 0904b83dd1a0..d99c9312e9a1 100644 --- a/tests/stdlib/tgenast.nim +++ b/tests/stdlib/tgenast.nim @@ -1,8 +1,13 @@ +discard """ + matrix: "--mm:orc; --mm:refc" +""" + # xxx also test on js import std/genasts import std/macros from std/strformat import `&` +import std/assertions import ./mgenast proc main = diff --git a/tests/stdlib/tgetaddrinfo.nim b/tests/stdlib/tgetaddrinfo.nim index ed8ec8b68641..a8bcecb0c593 100644 --- a/tests/stdlib/tgetaddrinfo.nim +++ b/tests/stdlib/tgetaddrinfo.nim @@ -6,6 +6,7 @@ discard """ # bug: https://github.com/nim-lang/Nim/issues/10198 import nativesockets +import std/assertions block DGRAM_UDP: let aiList = getAddrInfo("127.0.0.1", 999.Port, AF_INET, SOCK_DGRAM, IPPROTO_UDP) diff --git a/tests/stdlib/tgetfileinfo.nim b/tests/stdlib/tgetfileinfo.nim index 099ce1c22450..0f21622d0bc5 100644 --- a/tests/stdlib/tgetfileinfo.nim +++ b/tests/stdlib/tgetfileinfo.nim @@ -4,6 +4,7 @@ discard """ """ import os, strutils +import std/syncio # Cases # 1 - String : Existing File : Symlink true # 2 - String : Existing File : Symlink false diff --git a/tests/stdlib/tgetprotobyname.nim b/tests/stdlib/tgetprotobyname.nim index 0b60d059f4f7..e524510b283e 100644 --- a/tests/stdlib/tgetprotobyname.nim +++ b/tests/stdlib/tgetprotobyname.nim @@ -1,8 +1,5 @@ import nativesockets - -when not defined(netbsd): - # Ref: https://github.com/nim-lang/Nim/issues/15452 - NetBSD doesn't define an `ip` protocol - doAssert getProtoByName("ip") == 0 +import std/assertions doAssert getProtoByName("ipv6") == 41 doAssert getProtoByName("tcp") == 6 diff --git a/tests/stdlib/tglobs.nim b/tests/stdlib/tglobs.nim index 739a127f85ae..69ff31938b2a 100644 --- a/tests/stdlib/tglobs.nim +++ b/tests/stdlib/tglobs.nim @@ -1,4 +1,5 @@ import std/private/globs +import std/assertions template main = when defined(windows): diff --git a/tests/stdlib/thashes.nim b/tests/stdlib/thashes.nim index 46576ef12ae9..caae79213f40 100644 --- a/tests/stdlib/thashes.nim +++ b/tests/stdlib/thashes.nim @@ -4,6 +4,7 @@ discard """ import std/hashes from stdtest/testutils import disableVm, whenVMorJs +import std/assertions when not defined(js) and not defined(cpp): block: diff --git a/tests/stdlib/theapqueue.nim b/tests/stdlib/theapqueue.nim index 3b68166afd0f..bb40b6f932b3 100644 --- a/tests/stdlib/theapqueue.nim +++ b/tests/stdlib/theapqueue.nim @@ -1,5 +1,5 @@ import std/heapqueue - +import std/assertions proc toSortedSeq[T](h: HeapQueue[T]): seq[T] = var tmp = h diff --git a/tests/stdlib/thighlite.nim b/tests/stdlib/thighlite.nim index 4113a6a80e84..5134215c1c8e 100644 --- a/tests/stdlib/thighlite.nim +++ b/tests/stdlib/thighlite.nim @@ -12,6 +12,12 @@ block: # Nim tokenizing @[("\"\"\"ok1\\nok2\\nok3\"\"\"", gtLongStringLit) ]) + test "whitespace at beginning of line is preserved": + check(" discard 1".tokenize(langNim) == + @[(" ", gtWhitespace), ("discard", gtKeyword), (" ", gtWhitespace), + ("1", gtDecNumber) + ]) + block: # Cmd (shell) tokenizing test "cmd with dollar and output": check( diff --git a/tests/stdlib/thtmlparser.nim b/tests/stdlib/thtmlparser.nim index f35785b252d7..a27d41fe61bd 100644 --- a/tests/stdlib/thtmlparser.nim +++ b/tests/stdlib/thtmlparser.nim @@ -11,7 +11,7 @@ import htmlparser import xmltree import strutils from streams import newStringStream - +import std/assertions block t2813: const diff --git a/tests/stdlib/thttpclient.nim b/tests/stdlib/thttpclient.nim index 8563a0c260f8..d2fec6eece4d 100644 --- a/tests/stdlib/thttpclient.nim +++ b/tests/stdlib/thttpclient.nim @@ -14,6 +14,8 @@ from net import TimeoutError import nativesockets, os, httpclient, asyncdispatch +import std/[assertions, syncio] + const manualTests = false proc makeIPv6HttpServer(hostname: string, port: Port, diff --git a/tests/stdlib/thttpclient_ssl.nim b/tests/stdlib/thttpclient_ssl.nim index 3acdacfe3697..feacd3e57617 100644 --- a/tests/stdlib/thttpclient_ssl.nim +++ b/tests/stdlib/thttpclient_ssl.nim @@ -1,5 +1,5 @@ discard """ - cmd: "nim $target --threads:on -d:ssl $options $file" + cmd: "nim $target --mm:refc -d:ssl $options $file" disabled: "openbsd" """ @@ -15,7 +15,7 @@ discard """ when not defined(windows): # Disabled on Windows due to old OpenSSL version - + import std/[formatfloat, syncio] import httpclient, net, diff --git a/tests/stdlib/thttpclient_standalone.nim b/tests/stdlib/thttpclient_standalone.nim index 362b1cb861f1..2f432eedec17 100644 --- a/tests/stdlib/thttpclient_standalone.nim +++ b/tests/stdlib/thttpclient_standalone.nim @@ -4,6 +4,8 @@ discard """ import asynchttpserver, httpclient, asyncdispatch, strutils, net +import std/assertions + block: # bug #16436 proc startServer(): AsyncHttpServer = result = newAsyncHttpServer() diff --git a/tests/stdlib/thttpcore.nim b/tests/stdlib/thttpcore.nim index 6f88e953602e..3b6b1efa0e28 100644 --- a/tests/stdlib/thttpcore.nim +++ b/tests/stdlib/thttpcore.nim @@ -1,4 +1,5 @@ import httpcore, strutils +import std/assertions block: block HttpCode: diff --git a/tests/stdlib/tio.nim b/tests/stdlib/tio.nim index 0da64f9c26de..0e20d64958cb 100644 --- a/tests/stdlib/tio.nim +++ b/tests/stdlib/tio.nim @@ -2,6 +2,7 @@ import std/os from stdtest/specialpaths import buildDir +import std/[assertions, syncio] block: # readChars let file = buildDir / "D20201118T205105.txt" diff --git a/tests/stdlib/tisolation.nim b/tests/stdlib/tisolation.nim index c3857f483daf..18b83ea2ef3c 100644 --- a/tests/stdlib/tisolation.nim +++ b/tests/stdlib/tisolation.nim @@ -4,7 +4,7 @@ discard """ """ import std/[isolation, json] - +import std/[assertions, objectdollar] proc main(moveZeroesOut: static bool) = diff --git a/tests/stdlib/tjsbigints.nim b/tests/stdlib/tjsbigints.nim index fcf699c67251..29b0ac3e71c5 100644 --- a/tests/stdlib/tjsbigints.nim +++ b/tests/stdlib/tjsbigints.nim @@ -2,7 +2,7 @@ discard """ targets: "js" """ -import std/jsbigints +import std/[jsbigints, assertions] let big1: JsBigInt = big"2147483647" diff --git a/tests/stdlib/tjson.nim b/tests/stdlib/tjson.nim index 336558ff38c9..a60d45aab442 100644 --- a/tests/stdlib/tjson.nim +++ b/tests/stdlib/tjson.nim @@ -1,4 +1,5 @@ discard """ + matrix: "--mm:refc" targets: "c cpp js" """ @@ -13,6 +14,7 @@ when not defined(js): import std/streams import stdtest/testutils from std/fenv import epsilon +import std/[assertions, objectdollar] proc testRoundtrip[T](t: T, expected: string) = # checks that `T => json => T2 => json2` is such that json2 = json @@ -49,7 +51,7 @@ for i in 0 .. 10000: except: discard # memory diff should less than 4M -doAssert(abs(getOccupiedMem() - startMemory) < 4 * 1024 * 1024) +doAssert(abs(getOccupiedMem() - startMemory) < 4 * 1024 * 1024) # todo fixme doesn;t work for ORC # test `$` diff --git a/tests/stdlib/tjsonmacro.nim b/tests/stdlib/tjsonmacro.nim index 9b59c7dc3aa5..9c1fa833d29c 100644 --- a/tests/stdlib/tjsonmacro.nim +++ b/tests/stdlib/tjsonmacro.nim @@ -4,6 +4,7 @@ discard """ """ import json, strutils, options, tables +import std/assertions # The definition of the `%` proc needs to be here, since the `% c` calls below # can only find our custom `%` proc for `Pix` if defined in global scope. diff --git a/tests/stdlib/tjsonutils.nim b/tests/stdlib/tjsonutils.nim index d5809ee73286..54cb69560452 100644 --- a/tests/stdlib/tjsonutils.nim +++ b/tests/stdlib/tjsonutils.nim @@ -7,6 +7,7 @@ import std/json from std/math import isNaN, signbit from std/fenv import epsilon from stdtest/testutils import whenRuntimeJs +import std/[assertions, objectdollar] proc testRoundtrip[T](t: T, expected: string) = # checks that `T => json => T2 => json2` is such that json2 = json diff --git a/tests/stdlib/tlists.nim b/tests/stdlib/tlists.nim index 00c5b1a27bab..701fb7974813 100644 --- a/tests/stdlib/tlists.nim +++ b/tests/stdlib/tlists.nim @@ -3,6 +3,7 @@ discard """ """ import std/[lists, sequtils] +import std/assertions const data = [1, 2, 3, 4, 5, 6] diff --git a/tests/stdlib/tlocks.nim b/tests/stdlib/tlocks.nim index 0815c5d013a2..9ce9afd1309b 100644 --- a/tests/stdlib/tlocks.nim +++ b/tests/stdlib/tlocks.nim @@ -5,6 +5,7 @@ discard """ #bug #6049 import uselocks +import std/assertions var m = createMyType[int]() doAssert m.use() == 3 diff --git a/tests/stdlib/tmacros.nim b/tests/stdlib/tmacros.nim index 299eac49b532..7ec2fed9f6fd 100644 --- a/tests/stdlib/tmacros.nim +++ b/tests/stdlib/tmacros.nim @@ -5,6 +5,7 @@ See also: ]# import std/macros +import std/assertions block: # hasArgOfName macro m(u: untyped): untyped = @@ -144,3 +145,15 @@ block: # extractDocCommentsAndRunnables proc c() {.checkComments("Hello world").} = ## Hello world + +block: # bug #19020 + type + foo = object + + template typ(T:typedesc) {.pragma.} + + proc bar() {.typ: foo.} = discard + + static: + doAssert $bar.getCustomPragmaVal(typ) == "foo" + doAssert $bar.getCustomPragmaVal(typ) == "foo" diff --git a/tests/stdlib/tmarshal.nim b/tests/stdlib/tmarshal.nim index e539b1c3440c..f972332a240d 100644 --- a/tests/stdlib/tmarshal.nim +++ b/tests/stdlib/tmarshal.nim @@ -1,4 +1,9 @@ +discard """ + matrix: "--mm:orc; --mm:refc" +""" + import std/marshal +import std/[assertions, objectdollar] # TODO: add static tests @@ -136,6 +141,16 @@ block: let test = to[LegacyEntry](str) doAssert $test == """(numeric: "")""" +block: + let str = """{"numeric": null}""" + + type + LegacyEntry = object + numeric: seq[int] + + var test = to[LegacyEntry](str) + doAssert $test == """(numeric: @[])""" + # bug #16022 block: let p: proc (): string = proc (): string = "hello world" diff --git a/tests/stdlib/tmath.nim b/tests/stdlib/tmath.nim index 5e501c09bbb5..66c1f8ca0913 100644 --- a/tests/stdlib/tmath.nim +++ b/tests/stdlib/tmath.nim @@ -7,6 +7,8 @@ discard """ # but it requires disabling certain lines with `when not defined(nimTmathCase2)` import std/math +import std/assertions + # Function for approximate comparison of floats proc `==~`(x, y: float): bool = abs(x - y) < 1e-9 diff --git a/tests/stdlib/tmd5.nim b/tests/stdlib/tmd5.nim index 4017ac6779f4..254eefea9254 100644 --- a/tests/stdlib/tmd5.nim +++ b/tests/stdlib/tmd5.nim @@ -3,6 +3,7 @@ discard """ """ import md5 +import std/assertions proc main() {.raises: [].} = doAssert(getMD5("Franz jagt im komplett verwahrlosten Taxi quer durch Bayern") == diff --git a/tests/stdlib/tmemfiles1.nim b/tests/stdlib/tmemfiles1.nim index 21a65369f74a..33657256c012 100644 --- a/tests/stdlib/tmemfiles1.nim +++ b/tests/stdlib/tmemfiles1.nim @@ -1,4 +1,6 @@ import memfiles, os +import std/syncio + var mm: MemFile fn = "test.mmap" diff --git a/tests/stdlib/tmemfiles2.nim b/tests/stdlib/tmemfiles2.nim index 1b249898eece..6fee3c1ae3af 100644 --- a/tests/stdlib/tmemfiles2.nim +++ b/tests/stdlib/tmemfiles2.nim @@ -4,6 +4,9 @@ discard """ Half read size: 10 Data: Hello''' """ import memfiles, os +import std/syncio + + const fn = "test.mmap" var diff --git a/tests/stdlib/tmemlinesBuf.nim b/tests/stdlib/tmemlinesBuf.nim index 3f0bd5182d8f..7bd89d4f2cbd 100644 --- a/tests/stdlib/tmemlinesBuf.nim +++ b/tests/stdlib/tmemlinesBuf.nim @@ -1,4 +1,4 @@ -import memfiles +import std/[memfiles, assertions] var inp = memfiles.open("tests/stdlib/tmemlinesBuf.nim") var buffer: string = "" var lineCount = 0 diff --git a/tests/stdlib/tmemmapstreams.nim b/tests/stdlib/tmemmapstreams.nim index dd011d7773da..9cfae62c7d77 100644 --- a/tests/stdlib/tmemmapstreams.nim +++ b/tests/stdlib/tmemmapstreams.nim @@ -12,6 +12,8 @@ Readed line: Hello! Position after reading line: 7''' """ import os, streams, memfiles +import std/syncio + const fn = "test.mmapstream" var diff --git a/tests/stdlib/tmemory.nim b/tests/stdlib/tmemory.nim index 0349ba03507b..553037011d5b 100644 --- a/tests/stdlib/tmemory.nim +++ b/tests/stdlib/tmemory.nim @@ -1,3 +1,4 @@ +import std/assertions block: # cmpMem type diff --git a/tests/stdlib/tmersenne.nim b/tests/stdlib/tmersenne.nim index 54eb7b216b2c..64450a045751 100644 --- a/tests/stdlib/tmersenne.nim +++ b/tests/stdlib/tmersenne.nim @@ -1,4 +1,5 @@ import std/mersenne +import std/assertions template main() = var mt = newMersenneTwister(2525) diff --git a/tests/stdlib/tmimetypes.nim b/tests/stdlib/tmimetypes.nim index 6435309e1171..8263e37fdceb 100644 --- a/tests/stdlib/tmimetypes.nim +++ b/tests/stdlib/tmimetypes.nim @@ -3,6 +3,9 @@ discard """ """ import std/mimetypes +import std/assertions + + template main() = var m = newMimetypes() doAssert m.getMimetype("mp4") == "video/mp4" diff --git a/tests/stdlib/tmisc_issues.nim b/tests/stdlib/tmisc_issues.nim new file mode 100644 index 000000000000..b5a02e614b66 --- /dev/null +++ b/tests/stdlib/tmisc_issues.nim @@ -0,0 +1,19 @@ +discard """ + targets: "c cpp js" +""" + +import std/assertions + +# bug #20227 +type + Data = object + id: int + + Test = distinct Data + + Object = object + data: Test + + +var x: Object = Object(data: Test(Data(id: 12))) +doAssert Data(x.data).id == 12 diff --git a/tests/stdlib/tmitems.nim b/tests/stdlib/tmitems.nim index c0ced7cab407..171604e33a09 100644 --- a/tests/stdlib/tmitems.nim +++ b/tests/stdlib/tmitems.nim @@ -62,6 +62,7 @@ block: block: var x = "foobar" + prepareMutation(x) var y = cast[cstring](addr x[0]) for c in y.mitems: inc c @@ -75,6 +76,7 @@ block: block: var x = "foobar" + prepareMutation(x) var y = cast[cstring](addr x[0]) for i, c in y.mpairs: inc c, i diff --git a/tests/stdlib/tmonotimes.nim b/tests/stdlib/tmonotimes.nim index 2933bb68664c..f10fef591c24 100644 --- a/tests/stdlib/tmonotimes.nim +++ b/tests/stdlib/tmonotimes.nim @@ -3,6 +3,7 @@ discard """ """ import std/[monotimes, times] +import std/assertions let d = initDuration(nanoseconds = 10) let t1 = getMonoTime() diff --git a/tests/stdlib/tnativesockets.nim b/tests/stdlib/tnativesockets.nim index 6a1a00881e73..b1bbf32c2c08 100644 --- a/tests/stdlib/tnativesockets.nim +++ b/tests/stdlib/tnativesockets.nim @@ -1,5 +1,6 @@ import std/nativesockets import stdtest/testutils +import std/assertions block: let hostname = getHostname() diff --git a/tests/stdlib/tnet.nim b/tests/stdlib/tnet.nim index 4ec62d88f7c5..06ff44c3dba2 100644 --- a/tests/stdlib/tnet.nim +++ b/tests/stdlib/tnet.nim @@ -4,6 +4,7 @@ outputsub: "" import net, nativesockets import unittest +import std/assertions block: # isIpAddress tests block: # 127.0.0.1 is valid diff --git a/tests/stdlib/tnetdial.nim b/tests/stdlib/tnetdial.nim index b836fb78d566..3b8276d6f620 100644 --- a/tests/stdlib/tnetdial.nim +++ b/tests/stdlib/tnetdial.nim @@ -5,6 +5,7 @@ discard """ """ import os, net, nativesockets, asyncdispatch +import std/[assertions] ## Test for net.dial diff --git a/tests/stdlib/tntpath.nim b/tests/stdlib/tntpath.nim new file mode 100644 index 000000000000..dce0cf6f8171 --- /dev/null +++ b/tests/stdlib/tntpath.nim @@ -0,0 +1,46 @@ +import std/private/ntpath +import std/assertions + +block: # From Python's `Lib/test/test_ntpath.py` + doAssert splitDrive(r"c:\foo\bar") == (r"c:", r"\foo\bar") + doAssert splitDrive(r"c:/foo/bar") == (r"c:", r"/foo/bar") + doAssert splitDrive(r"\\conky\mountpoint\foo\bar") == (r"\\conky\mountpoint", r"\foo\bar") + doAssert splitDrive(r"//conky/mountpoint/foo/bar") == (r"//conky/mountpoint", r"/foo/bar") + doAssert splitDrive(r"\\\conky\mountpoint\foo\bar") == (r"", r"\\\conky\mountpoint\foo\bar") + doAssert splitDrive(r"///conky/mountpoint/foo/bar") == (r"", r"///conky/mountpoint/foo/bar") + doAssert splitDrive(r"\\conky\\mountpoint\foo\bar") == (r"", r"\\conky\\mountpoint\foo\bar") + doAssert splitDrive(r"//conky//mountpoint/foo/bar") == (r"", r"//conky//mountpoint/foo/bar") + # Issue #19911: UNC part containing U+0130 + doAssert splitDrive(r"//conky/MOUNTPOİNT/foo/bar") == (r"//conky/MOUNTPOİNT", r"/foo/bar") + # gh-81790: support device namespace, including UNC drives. + doAssert splitDrive(r"//?/c:") == (r"//?/c:", r"") + doAssert splitDrive(r"//?/c:/") == (r"//?/c:", r"/") + doAssert splitDrive(r"//?/c:/dir") == (r"//?/c:", r"/dir") + doAssert splitDrive(r"//?/UNC") == (r"", r"//?/UNC") + doAssert splitDrive(r"//?/UNC/") == (r"", r"//?/UNC/") + doAssert splitDrive(r"//?/UNC/server/") == (r"//?/UNC/server/", r"") + doAssert splitDrive(r"//?/UNC/server/share") == (r"//?/UNC/server/share", r"") + doAssert splitDrive(r"//?/UNC/server/share/dir") == (r"//?/UNC/server/share", r"/dir") + doAssert splitDrive(r"//?/VOLUME{00000000-0000-0000-0000-000000000000}/spam") == (r"//?/VOLUME{00000000-0000-0000-0000-000000000000}", r"/spam") + doAssert splitDrive(r"//?/BootPartition/") == (r"//?/BootPartition", r"/") + + doAssert splitDrive(r"\\?\c:") == (r"\\?\c:", r"") + doAssert splitDrive(r"\\?\c:\") == (r"\\?\c:", r"\") + doAssert splitDrive(r"\\?\c:\dir") == (r"\\?\c:", r"\dir") + doAssert splitDrive(r"\\?\UNC") == (r"", r"\\?\UNC") + doAssert splitDrive(r"\\?\UNC\") == (r"", r"\\?\UNC\") + doAssert splitDrive(r"\\?\UNC\server\") == (r"\\?\UNC\server\", r"") + doAssert splitDrive(r"\\?\UNC\server\share") == (r"\\?\UNC\server\share", r"") + doAssert splitDrive(r"\\?\UNC\server\share\dir") == (r"\\?\UNC\server\share", r"\dir") + doAssert splitDrive(r"\\?\VOLUME{00000000-0000-0000-0000-000000000000}\spam") == (r"\\?\VOLUME{00000000-0000-0000-0000-000000000000}", r"\spam") + doAssert splitDrive(r"\\?\BootPartition\") == (r"\\?\BootPartition", r"\") + +block: + doAssert splitDrive(r"C:") == (r"C:", r"") + doAssert splitDrive(r"C:\") == (r"C:", r"\") + doAssert splitDrive(r"non/absolute/path") == (r"", r"non/absolute/path") + + # Special for `\`-rooted paths on Windows. I don't know if this is correct, + # rbut `\` is not recognized as a drive, in contrast to `C:` or `\?\c:`. + # This behavior is the same for Python's `splitdrive` function. + doAssert splitDrive(r"\\") == (r"", r"\\") diff --git a/tests/stdlib/tobjectdollar.nim b/tests/stdlib/tobjectdollar.nim new file mode 100644 index 000000000000..cf78fa255af7 --- /dev/null +++ b/tests/stdlib/tobjectdollar.nim @@ -0,0 +1,14 @@ +discard """ + matrix: "-d:nimPreviewSlimSystem" +""" + +import std/assertions + +type Foo = object + a, b: int + +let x = Foo(a: 23, b: 45) +doAssert not compiles($x) +import std/objectdollar +doAssert compiles($x) +doAssert $x == "(a: 23, b: 45)" diff --git a/tests/stdlib/toids.nim b/tests/stdlib/toids.nim index f162dbe57ccc..95161415d92a 100644 --- a/tests/stdlib/toids.nim +++ b/tests/stdlib/toids.nim @@ -1,6 +1,15 @@ -import std/oids +discard """ + matrix: "--mm:refc; --mm:orc" +""" +import std/oids +import std/assertions block: # genOid let x = genOid() - doAssert ($x).len == 24 + doAssert ($x).len == 32 + +block: + let x = genOid() + let y = parseOid(cstring($x)) + doAssert x == y diff --git a/tests/stdlib/topenssl.nim b/tests/stdlib/topenssl.nim index 4c38b42164c3..3209437de272 100644 --- a/tests/stdlib/topenssl.nim +++ b/tests/stdlib/topenssl.nim @@ -1,5 +1,6 @@ import std/wordwrap import openssl +import std/assertions const PubKey = r"MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAknKWvrdnncCIzBnIGrZ5qtZrPH+Yo3t7ag9WZIu6Gmc/JgIDDaZhJeyGW0YSnifeAEhooWvM4jDWhTEARzktalSHqYtmwI/1Oxwp6NTYH8akMe2LCpZ5pX9FVA6m9o2tkbdXatbDKRqeD4UA8Ow7Iyrdo6eb1SU8vk+26i+uXHTtsb25p8uf2ppOJrJCy+1vr8Gsnuwny1UdoYZTxMsxRFPf+UX/LrSXMHVq/oPVa3SJ4VHMpYrG/httAugVP6K58xiZ93jst63/dd0JL85mWJu1uS3uz92aL5O97xzth3wR4BbdmDUlN4LuTIwi6DtEcC7gUOTnOzH4zgp2b5RyHwIDAQAB" const PrivateKey = r"MIIEpAIBAAKCAQEAknKWvrdnncCIzBnIGrZ5qtZrPH+Yo3t7ag9WZIu6Gmc/JgIDDaZhJeyGW0YSnifeAEhooWvM4jDWhTEARzktalSHqYtmwI/1Oxwp6NTYH8akMe2LCpZ5pX9FVA6m9o2tkbdXatbDKRqeD4UA8Ow7Iyrdo6eb1SU8vk+26i+uXHTtsb25p8uf2ppOJrJCy+1vr8Gsnuwny1UdoYZTxMsxRFPf+UX/LrSXMHVq/oPVa3SJ4VHMpYrG/httAugVP6K58xiZ93jst63/dd0JL85mWJu1uS3uz92aL5O97xzth3wR4BbdmDUlN4LuTIwi6DtEcC7gUOTnOzH4zgp2b5RyHwIDAQABAoIBACSOxmLFlfAjaALLTNCeTLEA5bQshgYJhT1sprxixQpiS7lJN0npBsdYzBFs5KjmetzHNpdVOcgdOO/204L0Gwo4H8WLLxNS3HztAulEeM813zc3fUYfWi6eHshk//j8VR/TDNd21TElm99z7FA4KGsXAE0iQhxrN0aqz5aWYIhjprtHA5KxXIiESnTkof5Cud8oXEnPiwPGNhq93QeQzh7xQIKSaDKBcdAa6edTFhzc4RLUQRfrik/GqJzouEDQ9v6H/uiOLTB3FxxwErQIf6dvSVhD9gs1nSLQfyj3S2Hxe9S2zglTl07EsawTQUxtVQkdZUOok67c7CPBxecZ2wECgYEA2c31gr/UJwczT+P/AE52GkHHETXMxqE3Hnh9n4CitfAFSD5X0VwZvGjZIlln2WjisTd92Ymf65eDylX2kCm93nzZ2GfXgS4zl4oY1N87+VeNQlx9f2+6GU7Hs0HFdfu8bGd+0sOuWA1PFqQCobxCACMPTkuzsG9M7knUTN59HS8CgYEArCEoP4ReYoOFveXUE0AteTPb4hryvR9VDEolP+LMoiPe8AzBMeB5fP493TPdjtnWmrPCXNLc7UAFSj2CZsRhau4PuiqnNrsb5iz/7iXVl3E8wZvS4w7WYpO4m33L0cijA6MdcdqilQu4Z5tw4nG45lAW9UYyOc9D4hJTzgtGHhECgYA6QyDoj931brSoK0ocT+DB11Sj4utbOuberMaV8zgTSRhwodSl+WgdAUMMMDRacPcrBrgQiAMSZ15msqYZHEFhEa7Id8arFKvSXquTzf9iDKyJ0unzO/ThLjS3W+GxVNyrdufzA0tQ3IaKfOcDUrOpC7fdbtyrVqqSl4dF5MI9GwKBgQCl3OF6qyOEDDZgsUk1L59h7k3QR6VmBf4e9IeGUxZamvQlHjU/yY1nm1mjgGnbUB/SPKtqZKoMV6eBTVoNiuhQcItpGda9D3mnx+7p3T0/TBd+fJeuwcplfPDjrEktogcq5w/leQc3Ve7gr1EMcwb3r28f8/9L42QHQR/OKODs8QKBgQCFAvxDRPyYg7V/AgD9rt1KzXi4+b3Pls5NXZa2g/w+hmdhHUNxV5IGmHlqFnptGyshgYgQGxMMkW0iJ1j8nLamFnkbFQOp5/UKbdPLRKiB86oPpxsqYtPXucDUqEfcMsp57mD1CpGVODbspogFpSUvQpMECkhvI0XLMbolMdo53g==" diff --git a/tests/stdlib/toptions.nim b/tests/stdlib/toptions.nim index 71c52a07e49a..6065425b98a7 100644 --- a/tests/stdlib/toptions.nim +++ b/tests/stdlib/toptions.nim @@ -4,6 +4,9 @@ discard """ import std/[json, options] +import std/assertions +import std/objectdollar + # RefPerson is used to test that overloaded `==` operator is not called by # options. It is defined here in the global scope, because otherwise the test diff --git a/tests/stdlib/tos.nim b/tests/stdlib/tos.nim index b7816fd41dc4..bd91a3de9e6f 100644 --- a/tests/stdlib/tos.nim +++ b/tests/stdlib/tos.nim @@ -28,6 +28,7 @@ Raises import os, strutils, pathnorm from stdtest/specialpaths import buildDir +import std/[syncio, assertions] block fileOperations: let files = @["these.txt", "are.x", "testing.r", "files.q"] @@ -156,6 +157,9 @@ block fileOperations: doAssert fileExists("../dest/a/file.txt") removeDir("../dest") + # createDir should not fail if `dir` is empty + createDir("") + # Symlink handling in `copyFile`, `copyFileWithPermissions`, `copyFileToDir`, # `copyDir`, `copyDirWithPermissions`, `moveFile`, and `moveDir`. block: @@ -508,7 +512,11 @@ block ospaths: doAssert relativePath("/Users/me/bar/z.nim", "/Users/other/bad", '/') == "../../me/bar/z.nim" doAssert relativePath("/Users/me/bar/z.nim", "/Users/other", '/') == "../me/bar/z.nim" - doAssert relativePath("/Users///me/bar//z.nim", "//Users/", '/') == "me/bar/z.nim" + + # `//` is a UNC path, `/` is the current working directory's drive, so can't + # run this test on Windows. + when not doslikeFileSystem: + doAssert relativePath("/Users///me/bar//z.nim", "//Users/", '/') == "me/bar/z.nim" doAssert relativePath("/Users/me/bar/z.nim", "/Users/me", '/') == "bar/z.nim" doAssert relativePath("", "/users/moo", '/') == "" doAssert relativePath("foo", "", '/') == "foo" @@ -707,3 +715,81 @@ block: # isAdmin if isAzure and defined(windows): doAssert isAdmin() # In Azure on POSIX tests run as a normal user if isAzure and defined(posix): doAssert not isAdmin() + +import std/sequtils + +when doslikeFileSystem: + import std/private/ntpath + + block: # Bug #19103 UNC paths + + # Easiest way of generating a valid, readable and writable UNC path + let tempDir = r"\\?\" & getTempDir() + doAssert dirExists tempDir + createDir tempDir / "test" + removeDir tempDir / "test" + createDir tempDir / "recursive" / "test" + removeDir tempDir / "recursive" / "test" + + let tempDir2 = getTempDir() + let (drive, pathNoDrive) = splitDrive(tempDir2) + setCurrentDir drive + doAssert cmpIgnoreCase(getCurrentDir().splitDrive.drive, drive) == 0 + + # Test `\Users` path syntax on Windows by stripping away drive. `\` + # resolves to the drive in current working directory. This drive will be + # the same as `tempDir2` because of the `setCurrentDir` above. + doAssert pathNoDrive[0] == '\\' + createDir pathNoDrive / "test" + doAssert dirExists pathNoDrive / "test" + removeDir pathNoDrive / "test" + + doAssert splitPath("//?/c:") == ("//?/c:", "") + + doAssert relativePath("//?/c:///Users//me", "//?/c:", '/') == "Users/me" + + doAssert parentDir(r"\\?\c:") == r"" + doAssert parentDir(r"//?/c:/Users") == r"\\?\c:" + doAssert parentDir(r"\\localhost\c$") == r"" + doAssert parentDir(r"\Users") == r"\" + + doAssert tailDir("//?/c:") == "" + doAssert tailDir("//?/c:/Users") == "Users" + doAssert tailDir(r"\\localhost\c$\Windows\System32") == r"Windows\System32" + + doAssert isRootDir("//?/c:") + doAssert isRootDir("//?/UNC/localhost/c$") + doAssert not isRootDir(r"\\?\c:\Users") + + doAssert parentDirs(r"C:\Users", fromRoot = true).toSeq == @[r"C:\", r"C:\Users"] + doAssert parentDirs(r"C:\Users", fromRoot = false).toSeq == @[r"C:\Users", r"C:"] + doAssert parentDirs(r"\\?\c:\Users", fromRoot = true).toSeq == + @[r"\\?\c:\", r"\\?\c:\Users"] + doAssert parentDirs(r"\\?\c:\Users", fromRoot = false).toSeq == + @[r"\\?\c:\Users", r"\\?\c:"] + doAssert parentDirs(r"//localhost/c$/Users", fromRoot = true).toSeq == + @[r"//localhost/c$/", r"//localhost/c$/Users"] + doAssert parentDirs(r"//?/UNC/localhost/c$/Users", fromRoot = false).toSeq == + @[r"//?/UNC/localhost/c$/Users", r"\\?\UNC\localhost\c$"] + doAssert parentDirs(r"\Users", fromRoot = true).toSeq == @[r"\", r"\Users"] + doAssert parentDirs(r"\Users", fromRoot = false).toSeq == @[r"\Users", r"\"] + + doAssert r"//?/c:" /../ "d/e" == r"\\?\c:\d\e" + doAssert r"//?/c:/Users" /../ "d/e" == r"\\?\c:\d\e" + doAssert r"\\localhost\c$" /../ "d/e" == r"\\localhost\c$\d\e" + + doAssert splitFile("//?/c:") == ("//?/c:", "", "") + doAssert splitFile("//?/c:/Users") == ("//?/c:", "Users", "") + doAssert splitFile(r"\\localhost\c$\test.txt") == (r"\\localhost\c$", "test", ".txt") + +else: + block: # parentDirs + doAssert parentDirs("/home", fromRoot=true).toSeq == @["/", "/home"] + doAssert parentDirs("/home", fromRoot=false).toSeq == @["/home", "/"] + doAssert parentDirs("home", fromRoot=true).toSeq == @["home"] + doAssert parentDirs("home", fromRoot=false).toSeq == @["home"] + + doAssert parentDirs("/home/user", fromRoot=true).toSeq == @["/", "/home/", "/home/user"] + doAssert parentDirs("/home/user", fromRoot=false).toSeq == @["/home/user", "/home", "/"] + doAssert parentDirs("home/user", fromRoot=true).toSeq == @["home/", "home/user"] + doAssert parentDirs("home/user", fromRoot=false).toSeq == @["home/user", "home"] diff --git a/tests/stdlib/tos_unc.nim b/tests/stdlib/tos_unc.nim index e55de11cef0b..fc74a4b9d312 100644 --- a/tests/stdlib/tos_unc.nim +++ b/tests/stdlib/tos_unc.nim @@ -4,6 +4,7 @@ discard """ # bug 10952, UNC paths import os +import std/assertions doAssert r"\\hostname\foo\bar" / "baz" == r"\\hostname\foo\bar\baz" doAssert r"\\?\C:\foo" / "bar" == r"\\?\C:\foo\bar" diff --git a/tests/stdlib/tosenv.nim b/tests/stdlib/tosenv.nim index 0a50031a116d..f7b3bb9d60b3 100644 --- a/tests/stdlib/tosenv.nim +++ b/tests/stdlib/tosenv.nim @@ -7,10 +7,14 @@ discard """ import std/os from std/sequtils import toSeq import stdtest/testutils +import std/assertions + +# "LATIN CAPITAL LETTER AE" in UTF-8 (0xc386) +const unicodeUtf8 = "\xc3\x86" template main = block: # delEnv, existsEnv, getEnv, envPairs - for val in ["val", ""]: # ensures empty val works too + for val in ["val", "", unicodeUtf8]: # ensures empty val works too const key = "NIM_TESTS_TOSENV_KEY" doAssert not existsEnv(key) @@ -45,9 +49,12 @@ template main = static: main() main() +when defined(windows): + proc c_wgetenv(env: WideCString): WideCString {.importc: "_wgetenv", header: "".} +proc c_getenv(env: cstring): cstring {.importc: "getenv", header: "".} + when not defined(js) and not defined(nimscript): block: # bug #18533 - proc c_getenv(env: cstring): cstring {.importc: "getenv", header: "".} var thr: Thread[void] proc threadFunc {.thread.} = putEnv("foo", "fooVal2") @@ -55,6 +62,97 @@ when not defined(js) and not defined(nimscript): doAssert getEnv("foo") == "fooVal1" createThread(thr, threadFunc) joinThreads(thr) - doAssert getEnv("foo") == $c_getenv("foo") + when defined(windows): + doAssert getEnv("foo") == $c_wgetenv("foo".newWideCString) + else: + doAssert getEnv("foo") == $c_getenv("foo".cstring) doAssertRaises(OSError): delEnv("foo=bar") + +when defined(windows) and not defined(nimscript): + import std/encodings + + proc c_putenv(env: cstring): int32 {.importc: "putenv", header: "".} + proc c_wputenv(env: WideCString): int32 {.importc: "_wputenv", header: "".} + + block: # Bug #20083 + # These test that `getEnv`, `putEnv` and `existsEnv` handle Unicode + # characters correctly. This means that module X in the process calling the + # CRT environment variable API will get the correct string. Raw CRT API + # calls below represent module X. + + # Getting an env. var. with unicode characters returns the correct UTF-8 + # encoded string. + block: + const envName = "twin_envvars1" + doAssert c_wputenv(newWideCString(envName & "=" & unicodeUtf8)) == 0 + doAssert existsEnv(envName) + doAssert getEnv(envName) == unicodeUtf8 + + # Putting an env. var. with unicode characters gives the correct UTF-16 + # encoded string from low-level routine. + block: + const envName = "twin_envvars2" + putEnv(envName, unicodeUtf8) + doAssert $c_wgetenv(envName.newWideCString) == unicodeUtf8 + + # Env. name containing Unicode characters is retrieved correctly + block: + const envName = unicodeUtf8 & "1" + doAssert c_wputenv(newWideCString(envName & "=" & unicodeUtf8)) == 0 + doAssert existsEnv(envName) + doAssert getEnv(envName) == unicodeUtf8 + + # Env. name containing Unicode characters is set correctly + block: + const envName = unicodeUtf8 & "2" + putEnv(envName, unicodeUtf8) + doAssert existsEnv(envName) + doAssert $c_wgetenv(envName.newWideCString) == unicodeUtf8 + + # Env. name containing Unicode characters and empty value is set correctly + block: + const envName = unicodeUtf8 & "3" + putEnv(envName, "") + doAssert existsEnv(envName) + doAssert $c_wgetenv(envName.newWideCString) == "" + + # It's hard to test on Windows code pages, because there is no "change + # a process' locale" API. + if getCurrentEncoding(true) == "windows-1252": + const + unicodeAnsi = "\xc6" # `unicodeUtf8` in `windows-1252` encoding + + # Test that env. var. ANSI API has correct encoding + block: + const + envName = unicodeUtf8 & "4" + envNameAnsi = unicodeAnsi & "4" + putEnv(envName, unicodeUtf8) + doAssert $c_getenv(envNameAnsi.cstring) == unicodeAnsi + + block: + const + envName = unicodeUtf8 & "5" + envNameAnsi = unicodeAnsi & "5" + doAssert c_putenv((envNameAnsi & "=" & unicodeAnsi).cstring) == 0 + doAssert getEnv(envName) == unicodeUtf8 + + # Env. name containing Unicode characters and empty value is set correctly; + # and, if env. name. characters cannot be represented in codepage, don't + # raise an error. + # + # `win_setenv.nim` converts UTF-16 to ANSI when setting empty env. var. The + # windows-1250 locale has no representation of `abreveUtf8` below, so the + # conversion will fail, but this must not be fatal. It is expected that the + # routine ignores updating MBCS environment (`environ` global) and carries + # on. + block: + const + # "LATIN SMALL LETTER A WITH BREVE" in UTF-8 + abreveUtf8 = "\xc4\x83" + envName = abreveUtf8 & "6" + putEnv(envName, "") + doAssert existsEnv(envName) + doAssert $c_wgetenv(envName.newWideCString) == "" + doAssert getEnv(envName) == "" diff --git a/tests/stdlib/tosproc.nim b/tests/stdlib/tosproc.nim index c54e0d112ead..47fec2567485 100644 --- a/tests/stdlib/tosproc.nim +++ b/tests/stdlib/tosproc.nim @@ -9,6 +9,7 @@ because it'd need cleanup up stdout see also: tests/osproc/*.nim; consider merging those into a single test here (easier to factor and test more things as a single self contained test) ]# +import std/[assertions, syncio] when defined(case_testfile): # compiled test file for child process from posix import exitnow @@ -29,6 +30,12 @@ when defined(case_testfile): # compiled test file for child process case arg of "exit_0": if true: quit(0) + of "exit_1": + if true: quit(1) + of "exit_2": + if true: quit(2) + of "exit_42": + if true: quit(42) of "exitnow_139": if true: exitnow(139) of "c_exit2_139": @@ -115,6 +122,13 @@ else: # main driver runTest("c_exit2_139", 139) runTest("quit_139", 139) + block execCmdTest: + let output = compileNimProg("-d:release -d:case_testfile", "D20220705T221100") + doAssert execCmd(output & " exit_0") == 0 + doAssert execCmd(output & " exit_1") == 1 + doAssert execCmd(output & " exit_2") == 2 + doAssert execCmd(output & " exit_42") == 42 + import std/streams block execProcessTest: diff --git a/tests/stdlib/tosprocterminate.nim b/tests/stdlib/tosprocterminate.nim index 8e9041b81c0d..08b379569fdc 100644 --- a/tests/stdlib/tosprocterminate.nim +++ b/tests/stdlib/tosprocterminate.nim @@ -5,6 +5,7 @@ discard """ """ import os, osproc, times, std / monotimes +import std/assertions when defined(windows): const ProgramWhichDoesNotEnd = "notepad" diff --git a/tests/stdlib/tpackedsets.nim b/tests/stdlib/tpackedsets.nim index d0149adc58d6..2c69f6b1bef8 100644 --- a/tests/stdlib/tpackedsets.nim +++ b/tests/stdlib/tpackedsets.nim @@ -4,6 +4,8 @@ import std/sets import sequtils import algorithm +import std/assertions + block basicIntSetTests: var y = initPackedSet[int]() y.incl(1) diff --git a/tests/stdlib/tparsecfg.nim b/tests/stdlib/tparsecfg.nim index b2e57ac3d184..16f12bc9e5eb 100644 --- a/tests/stdlib/tparsecfg.nim +++ b/tests/stdlib/tparsecfg.nim @@ -3,6 +3,7 @@ discard """ """ import parsecfg, streams, sequtils +import std/assertions when not defined(js): from stdtest/specialpaths import buildDir diff --git a/tests/stdlib/tparsecsv.nim b/tests/stdlib/tparsecsv.nim index 0d004d45d0bb..a879019f6814 100644 --- a/tests/stdlib/tparsecsv.nim +++ b/tests/stdlib/tparsecsv.nim @@ -1,5 +1,6 @@ include parsecsv import strutils, os +import std/assertions block: # Tests for reading the header row let content = "\nOne,Two,Three,Four\n1,2,3,4\n10,20,30,40,\n100,200,300,400\n" diff --git a/tests/stdlib/tparsesql.nim b/tests/stdlib/tparsesql.nim index 8ef67f5dcd14..cfd8ad14825a 100644 --- a/tests/stdlib/tparsesql.nim +++ b/tests/stdlib/tparsesql.nim @@ -2,6 +2,7 @@ discard """ targets: "c js" """ import parsesql +import std/assertions doAssert treeRepr(parseSql("INSERT INTO STATS VALUES (10, 5.5); ") ) == """ diff --git a/tests/stdlib/tparseutils.nim b/tests/stdlib/tparseutils.nim index db7a0ac8d14c..084a85dac981 100644 --- a/tests/stdlib/tparseutils.nim +++ b/tests/stdlib/tparseutils.nim @@ -1,4 +1,5 @@ -import std/[parseutils, sequtils, sugar] +import std/[parseutils, sequtils, sugar, formatfloat] +import std/assertions let input = "$test{} $this is ${an{ example}} " diff --git a/tests/stdlib/tparsopt.nim b/tests/stdlib/tparsopt.nim index 54a470cb30b3..f3a9a97982d6 100644 --- a/tests/stdlib/tparsopt.nim +++ b/tests/stdlib/tparsopt.nim @@ -9,6 +9,8 @@ disabled: true import parseopt +import std/[assertions, syncio] + proc writeHelp() = writeLine(stdout, "Usage: tparsopt [options] filename [options]") diff --git a/tests/stdlib/tpathnorm.nim b/tests/stdlib/tpathnorm.nim new file mode 100644 index 000000000000..1cd913084861 --- /dev/null +++ b/tests/stdlib/tpathnorm.nim @@ -0,0 +1,35 @@ +discard """ +""" + +import std/os +import std/assertions + +when doslikeFileSystem: + import std/pathnorm + + template initVars = + var state {.inject.} = 0 + var result {.inject.}: string + + block: # / -> / + initVars + addNormalizePath("//?/c:/./foo//bar/../baz", result, state, '/') + doAssert result == "//?/c:/foo/baz" + addNormalizePath("me", result, state, '/') + doAssert result == "//?/c:/foo/baz/me" + + block: # / -> \ + initVars + addNormalizePath(r"//?/c:/./foo//bar/../baz", result, state, '\\') + doAssert result == r"\\?\c:\foo\baz" + addNormalizePath("me", result, state, '\\') + doAssert result == r"\\?\c:\foo\baz\me" + + block: # Append path component to UNC drive + initVars + addNormalizePath(r"//?/c:", result, state, '\\') + doAssert result == r"\\?\c:" + addNormalizePath("Users", result, state, '\\') + doAssert result == r"\\?\c:\Users" + addNormalizePath("me", result, state, '\\') + doAssert result == r"\\?\c:\Users\me" diff --git a/tests/stdlib/tpegs.nim b/tests/stdlib/tpegs.nim index c3d8942cffb8..cbc8fe205bc3 100644 --- a/tests/stdlib/tpegs.nim +++ b/tests/stdlib/tpegs.nim @@ -54,7 +54,7 @@ Event parser output when defined(nimHasEffectsOf): {.experimental: "strictEffects".} -import std/[strutils, streams, pegs] +import std/[strutils, streams, pegs, assertions] const indent = " " @@ -158,6 +158,10 @@ block: privateAccess(NonTerminal) privateAccess(Captures) + if "test" =~ peg"s <- {{\ident}}": # bug #19104 + doAssert matches[0] == "test" + doAssert matches[1] == "test", $matches[1] + doAssert escapePeg("abc''def'") == r"'abc'\x27\x27'def'\x27" doAssert match("(a b c)", peg"'(' @ ')'") doAssert match("W_HI_Le", peg"\y 'while'") diff --git a/tests/stdlib/tposix.nim b/tests/stdlib/tposix.nim index 14f1fd6e2b33..ea0472c310cb 100644 --- a/tests/stdlib/tposix.nim +++ b/tests/stdlib/tposix.nim @@ -7,6 +7,7 @@ outputsub: "" when not defined(windows): import posix + import std/syncio var u: Utsname diff --git a/tests/stdlib/tprelude.nim b/tests/stdlib/tprelude.nim index a60bcf70a696..47f46b511dd2 100644 --- a/tests/stdlib/tprelude.nim +++ b/tests/stdlib/tprelude.nim @@ -8,6 +8,8 @@ when defined nimTestTpreludeCase1: else: include prelude +import std/assertions + template main() = doAssert toSeq(1..3) == @[1,2,3] static: main() diff --git a/tests/stdlib/tpunycode.nim b/tests/stdlib/tpunycode.nim index bb2b4beb4f63..dea6b2dc6a38 100644 --- a/tests/stdlib/tpunycode.nim +++ b/tests/stdlib/tpunycode.nim @@ -1,4 +1,5 @@ import punycode, std/unicode +import std/assertions doAssert(decode(encode("", "bücher")) == "bücher") doAssert(decode(encode("münchen")) == "münchen") diff --git a/tests/stdlib/tquit.nim b/tests/stdlib/tquit.nim index 81726fd7f611..12385e57b071 100644 --- a/tests/stdlib/tquit.nim +++ b/tests/stdlib/tquit.nim @@ -1,4 +1,5 @@ discard """ +disabled: true output: ''' just exiting... ''' @@ -7,6 +8,8 @@ joinable: false # Test `addQuitProc` (now deprecated by `addExitProc`) +import std/syncio + proc myExit() {.noconv.} = write(stdout, "just exiting...\n") diff --git a/tests/stdlib/trandom.nim b/tests/stdlib/trandom.nim index 61e858f86c7a..ef71c3442dec 100644 --- a/tests/stdlib/trandom.nim +++ b/tests/stdlib/trandom.nim @@ -2,7 +2,7 @@ discard """ joinable: false # to avoid messing with global rand state targets: "c js" """ - +import std/[assertions, formatfloat] import std/[random, math, stats, sets, tables] when not defined(js): import std/os diff --git a/tests/stdlib/trationals.nim b/tests/stdlib/trationals.nim index 0a3a95a9a908..cf2e92003f47 100644 --- a/tests/stdlib/trationals.nim +++ b/tests/stdlib/trationals.nim @@ -1,4 +1,5 @@ import std/[rationals, math] +import std/assertions template main() = var diff --git a/tests/stdlib/tre.nim b/tests/stdlib/tre.nim index 9f27f7db2222..3986934c49a3 100644 --- a/tests/stdlib/tre.nim +++ b/tests/stdlib/tre.nim @@ -1,4 +1,5 @@ import std/re +import std/assertions proc testAll() = doAssert match("(a b c)", rex"\( .* \)") diff --git a/tests/stdlib/tregex.nim b/tests/stdlib/tregex.nim index 21f4e6743959..cf80f81224c3 100644 --- a/tests/stdlib/tregex.nim +++ b/tests/stdlib/tregex.nim @@ -11,7 +11,7 @@ when defined(powerpc64): else: import re - + import std/syncio if "keyA = valueA" =~ re"\s*(\w+)\s*\=\s*(\w+)": write(stdout, "key: ", matches[0]) elif "# comment!" =~ re.re"\s*(\#.*)": diff --git a/tests/stdlib/tregistry.nim b/tests/stdlib/tregistry.nim index 8bf084f6d585..4956f81962af 100644 --- a/tests/stdlib/tregistry.nim +++ b/tests/stdlib/tregistry.nim @@ -5,6 +5,7 @@ discard """ when defined(windows): import std/registry + import std/assertions block: # bug #14010 let path = "Environment" diff --git a/tests/stdlib/trepr.nim b/tests/stdlib/trepr.nim index 82c991805278..c85ae2b2aa7c 100644 --- a/tests/stdlib/trepr.nim +++ b/tests/stdlib/trepr.nim @@ -7,6 +7,7 @@ discard """ from strutils import endsWith, contains, strip from std/macros import newLit +import std/assertions macro deb(a): string = newLit a.repr.strip macro debTyped(a: typed): string = newLit a.repr.strip diff --git a/tests/stdlib/tropes.nim b/tests/stdlib/tropes.nim index 5a9150a336a7..6d41e9e44aa3 100644 --- a/tests/stdlib/tropes.nim +++ b/tests/stdlib/tropes.nim @@ -3,6 +3,7 @@ discard """ """ import std/ropes +import std/assertions template main() = block: diff --git a/tests/stdlib/trst.nim b/tests/stdlib/trst.nim index 9787c13eb923..818f8b8dcf87 100644 --- a/tests/stdlib/trst.nim +++ b/tests/stdlib/trst.nim @@ -3,6 +3,8 @@ discard """ [Suite] RST parsing +[Suite] RST tables + [Suite] RST indentation [Suite] Warnings @@ -21,9 +23,13 @@ import ../../lib/packages/docutils/[rstgen, rst, rstast] import unittest, strutils import std/private/miscdollars import os +import std/[assertions, syncio] + +const preferMarkdown = {roPreferMarkdown, roSupportMarkdown, roNimFile, roSandboxDisabled} +const preferRst = {roSupportMarkdown, roNimFile, roSandboxDisabled} proc toAst(input: string, - rstOptions: RstParseOptions = {roPreferMarkdown, roSupportMarkdown, roNimFile, roSandboxDisabled}, + rstOptions: RstParseOptions = preferMarkdown, error: ref string = nil, warnings: ref seq[string] = nil): string = ## If `error` is nil then no errors should be generated. @@ -69,7 +75,7 @@ suite "RST parsing": """.toAst == dedent""" rnInner - rnHeadline level=1 + rnHeadline level=1 anchor='lexical-analysis' rnLeaf 'Lexical' rnLeaf ' ' rnLeaf 'Analysis' @@ -418,21 +424,21 @@ suite "RST parsing": .. Note:: deflist: >> quote continuation - """.toAst == expected) + """.toAst(rstOptions = preferRst) == expected) check(dedent""" .. Note:: deflist: >> quote continuation - """.toAst == expected) + """.toAst(rstOptions = preferRst) == expected) check(dedent""" .. Note:: deflist: >> quote >> continuation - """.toAst == expected) + """.toAst(rstOptions = preferRst) == expected) # spaces are not significant between `>`: check(dedent""" @@ -440,7 +446,7 @@ suite "RST parsing": deflist: > > quote > > continuation - """.toAst == expected) + """.toAst(rstOptions = preferRst) == expected) test "Markdown quoted blocks: de-indent handled well": check(dedent""" @@ -449,7 +455,7 @@ suite "RST parsing": > - y > > Paragraph. - """.toAst == dedent""" + """.toAst(rstOptions = preferRst) == dedent""" rnMarkdownBlockQuote rnMarkdownBlockQuoteItem quotationDepth=1 rnInner @@ -466,29 +472,174 @@ suite "RST parsing": rnLeaf '.' """) - test "option list has priority over definition list": + let expectCodeBlock = dedent""" + rnCodeBlock + [nil] + rnFieldList + rnField + rnFieldName + rnLeaf 'default-language' + rnFieldBody + rnLeaf 'Nim' + rnLiteralBlock + rnLeaf ' + let a = 1 + ```' + """ + + test "Markdown code blocks with more > 3 backticks": check(dedent""" - --defusages - file - -o set - """.toAst == + ```` + let a = 1 + ``` + ````""".toAst == expectCodeBlock) + + test "Markdown code blocks with ~~~": + check(dedent""" + ~~~ + let a = 1 + ``` + ~~~""".toAst == expectCodeBlock) + check(dedent""" + ~~~~~ + let a = 1 + ``` + ~~~~~""".toAst == expectCodeBlock) + + test "Markdown code blocks with Nim-specific arguments": + check(dedent""" + ```nim number-lines=1 test + let a = 1 + ```""".toAst == dedent""" - rnOptionList - rnOptionListItem order=1 - rnOptionGroup - rnLeaf '--' - rnLeaf 'defusages' - rnDescription - rnInner - rnLeaf 'file' - rnOptionListItem order=2 - rnOptionGroup - rnLeaf '-' - rnLeaf 'o' - rnDescription - rnLeaf 'set' + rnCodeBlock + rnDirArg + rnLeaf 'nim' + rnFieldList + rnField + rnFieldName + rnLeaf 'number-lines' + rnFieldBody + rnLeaf '1' + rnField + rnFieldName + rnLeaf 'test' + rnFieldBody + rnLiteralBlock + rnLeaf ' + let a = 1' """) + check(dedent""" + ```nim test = "nim c $1" number-lines = 1 + let a = 1 + ```""".toAst == + dedent""" + rnCodeBlock + rnDirArg + rnLeaf 'nim' + rnFieldList + rnField + rnFieldName + rnLeaf 'test' + rnFieldBody + rnLeaf '"nim c $1"' + rnField + rnFieldName + rnLeaf 'number-lines' + rnFieldBody + rnLeaf '1' + rnLiteralBlock + rnLeaf ' + let a = 1' + """) + + test "additional indentation < 4 spaces is handled fine": + check(dedent""" + Indentation + + ```nim + let a = 1 + ```""".toAst == + dedent""" + rnInner + rnParagraph + rnLeaf 'Indentation' + rnParagraph + rnCodeBlock + rnDirArg + rnLeaf 'nim' + [nil] + rnLiteralBlock + rnLeaf ' + let a = 1' + """) + # | | + # | \ indentation of exactly two spaces before 'let a = 1' + + test "no blank line is required before or after Markdown code block": + let inputBacktick = dedent""" + Some text + ``` + CodeBlock() + ``` + Other text""" + let inputTilde = dedent""" + Some text + ~~~~~~~~~ + CodeBlock() + ~~~~~~~~~ + Other text""" + let expected = dedent""" + rnInner + rnParagraph + rnLeaf 'Some' + rnLeaf ' ' + rnLeaf 'text' + rnParagraph + rnCodeBlock + [nil] + rnFieldList + rnField + rnFieldName + rnLeaf 'default-language' + rnFieldBody + rnLeaf 'Nim' + rnLiteralBlock + rnLeaf ' + CodeBlock()' + rnLeaf ' ' + rnLeaf 'Other' + rnLeaf ' ' + rnLeaf 'text' + """ + check inputBacktick.toAst == expected + check inputTilde.toAst == expected + + test "option list has priority over definition list": + for opt in [preferMarkdown, preferRst]: + check(dedent""" + --defusages + file + -o set + """.toAst(rstOptions = opt) == + dedent""" + rnOptionList + rnOptionListItem order=1 + rnOptionGroup + rnLeaf '--' + rnLeaf 'defusages' + rnDescription + rnInner + rnLeaf 'file' + rnOptionListItem order=2 + rnOptionGroup + rnLeaf '-' + rnLeaf 'o' + rnDescription + rnLeaf 'set' + """) + test "items of 1 option list can be separated by blank lines": check(dedent""" -a desc1 @@ -511,13 +662,13 @@ suite "RST parsing": rnLeaf 'desc2' """) - test "option list has priority over definition list": + test "definition list does not gobble up the following blocks": check(dedent""" defName defBody -b desc2 - """.toAst == + """.toAst(rstOptions = preferRst) == dedent""" rnInner rnDefList @@ -560,7 +711,7 @@ suite "RST parsing": notAcomment1 notAcomment2 - someParagraph""".toAst == + someParagraph""".toAst(rstOptions = preferRst) == dedent""" rnInner rnBlockQuote @@ -572,6 +723,25 @@ suite "RST parsing": rnLeaf 'someParagraph' """) + test "check that additional line right after .. ends comment (Markdown mode)": + # in Markdown small indentation does not matter so this should + # just be split to 2 paragraphs. + check(dedent""" + .. + + notAcomment1 + notAcomment2 + someParagraph""".toAst == + dedent""" + rnInner + rnInner + rnLeaf 'notAcomment1' + rnLeaf ' ' + rnLeaf 'notAcomment2' + rnParagraph + rnLeaf 'someParagraph' + """) + test "but blank lines after 2nd non-empty line don't end the comment": check(dedent""" .. @@ -590,7 +760,7 @@ suite "RST parsing": .. - someBlockQuote""".toAst == + someBlockQuote""".toAst(rstOptions = preferRst) == dedent""" rnInner rnAdmonition adType=note @@ -618,6 +788,233 @@ suite "RST parsing": rnLeaf 'code' """) +suite "RST tables": + + test "formatting in tables works": + check( + dedent""" + ========= === + `build` `a` + ========= === + """.toAst == + dedent""" + rnTable colCount=2 + rnTableRow + rnTableDataCell + rnInlineCode + rnDirArg + rnLeaf 'nim' + [nil] + rnLiteralBlock + rnLeaf 'build' + rnTableDataCell + rnInlineCode + rnDirArg + rnLeaf 'nim' + [nil] + rnLiteralBlock + rnLeaf 'a' + """) + + test "tables with slightly overflowed cells cause an error (1)": + var error = new string + check( + dedent""" + ====== ====== + Inputs Output + ====== ====== + """.toAst(error=error) == "") + check(error[] == "input(2, 2) Error: Illformed table: " & + "this word crosses table column from the right") + + test "tables with slightly overflowed cells cause an error (2)": + var error = new string + check("" == dedent""" + ===== ===== ====== + Input Output + ===== ===== ====== + False False False + ===== ===== ====== + """.toAst(error=error)) + check(error[] == "input(2, 8) Error: Illformed table: " & + "this word crosses table column from the right") + + test "tables with slightly underflowed cells cause an error": + var error = new string + check("" == dedent""" + ===== ===== ====== + Input Output + ===== ===== ====== + False False False + ===== ===== ====== + """.toAst(error=error)) + check(error[] == "input(2, 7) Error: Illformed table: " & + "this word crosses table column from the left") + + test "tables with unequal underlines should be reported (1)": + var error = new string + error[] = "none" + check("" == dedent""" + ===== ====== + Input Output + ===== ====== + False False + ===== ======= + """.toAst(error=error)) + check(error[] == "input(5, 14) Error: Illformed table: " & + "end of table column #2 should end at position 13") + + test "tables with unequal underlines should be reported (2)": + var error = new string + check("" == dedent""" + ===== ====== + Input Output + ===== ======= + False False + ===== ====== + """.toAst(error=error)) + check(error[] == "input(3, 14) Error: Illformed table: " & + "end of table column #2 should end at position 13") + + test "tables with empty first cells": + check( + dedent""" + = = = + x y z + t + = = = + """.toAst == + dedent""" + rnTable colCount=3 + rnTableRow + rnTableDataCell + rnLeaf 'x' + rnTableDataCell + rnInner + rnLeaf 'y' + rnLeaf ' ' + rnTableDataCell + rnInner + rnLeaf 'z' + rnLeaf ' ' + rnLeaf 't' + """) + + test "tables with spanning cells & separators": + check( + dedent""" + ===== ===== ====== + Inputs Output + ------------ ------ + A B A or B + ===== ===== ====== + False False False + True False True + ----- ----- ------ + False True True + True True True + ===== ===== ====== + """.toAst == + dedent""" + rnTable colCount=3 + rnTableRow + rnTableHeaderCell span=2 + rnLeaf 'Inputs' + rnTableHeaderCell span=1 + rnLeaf 'Output' + rnTableRow endsHeader + rnTableHeaderCell + rnLeaf 'A' + rnTableHeaderCell + rnLeaf 'B' + rnTableHeaderCell + rnInner + rnLeaf 'A' + rnLeaf ' ' + rnLeaf 'or' + rnLeaf ' ' + rnLeaf 'B' + rnTableRow + rnTableDataCell + rnLeaf 'False' + rnTableDataCell + rnLeaf 'False' + rnTableDataCell + rnLeaf 'False' + rnTableRow + rnTableDataCell span=1 + rnLeaf 'True' + rnTableDataCell span=1 + rnLeaf 'False' + rnTableDataCell span=1 + rnLeaf 'True' + rnTableRow + rnTableDataCell + rnLeaf 'False' + rnTableDataCell + rnLeaf 'True' + rnTableDataCell + rnLeaf 'True' + rnTableRow + rnTableDataCell + rnLeaf 'True' + rnTableDataCell + rnLeaf 'True' + rnTableDataCell + rnLeaf 'True' + """) + + test "tables with spanning cells with uneqal underlines cause an error": + var error = new string + check( + dedent""" + ===== ===== ====== + Inputs Output + ------------- ------ + A B A or B + ===== ===== ====== + """.toAst(error=error) == "") + check(error[] == "input(3, 1) Error: Illformed table: " & + "spanning underline does not match main table columns") + + let expTable = dedent""" + rnTable colCount=2 + rnTableRow + rnTableDataCell + rnLeaf 'Inputs' + rnTableDataCell + rnLeaf 'Output' + """ + + test "only tables with `=` columns specs are allowed (1)": + var warnings = new seq[string] + check( + dedent""" + ------ ------ + Inputs Output + ------ ------ + """.toAst(warnings=warnings) == + expTable) + check(warnings[] == + @["input(1, 1) Warning: RST style: " & + "only tables with `=` columns specification are allowed", + "input(3, 1) Warning: RST style: " & + "only tables with `=` columns specification are allowed"]) + + test "only tables with `=` columns specs are allowed (2)": + var warnings = new seq[string] + check( + dedent""" + ====== ====== + Inputs Output + ~~~~~~ ~~~~~~ + """.toAst(warnings=warnings) == + expTable) + check(warnings[] == + @["input(3, 1) Warning: RST style: "& + "only tables with `=` columns specification are allowed"]) + + suite "RST indentation": test "nested bullet lists": let input = dedent """ @@ -659,7 +1056,7 @@ suite "RST indentation": term2 Definition2 """ - check(input.toAst == dedent""" + check(input.toAst(rstOptions = preferRst) == dedent""" rnEnumList labelFmt=1) rnEnumItem rnAdmonition adType=hint @@ -762,6 +1159,85 @@ suite "RST indentation": # "template..." should be parsed as a definition list attached to ":test:": check inputWrong.toAst != ast + test "Markdown definition lists work in conjunction with bullet lists": + check(dedent""" + * some term + : the definition + + Paragraph.""".toAst == + dedent""" + rnInner + rnBulletList + rnBulletItem + rnMdDefList + rnDefItem + rnDefName + rnLeaf 'some' + rnLeaf ' ' + rnLeaf 'term' + rnDefBody + rnInner + rnLeaf 'the' + rnLeaf ' ' + rnLeaf 'definition' + rnParagraph + rnLeaf 'Paragraph' + rnLeaf '.' + """) + + test "Markdown definition lists work with blank lines and extra paragraphs": + check(dedent""" + Term1 + + : Definition1 + + Term2 *inline markup* + + : Definition2 + + Paragraph2 + + Term3 + : * point1 + * point2 + : term3definition2 + """.toAst == dedent""" + rnMdDefList + rnDefItem + rnDefName + rnLeaf 'Term1' + rnDefBody + rnInner + rnLeaf 'Definition1' + rnDefItem + rnDefName + rnLeaf 'Term2' + rnLeaf ' ' + rnEmphasis + rnLeaf 'inline' + rnLeaf ' ' + rnLeaf 'markup' + rnDefBody + rnParagraph + rnLeaf 'Definition2' + rnParagraph + rnLeaf 'Paragraph2' + rnDefItem + rnDefName + rnLeaf 'Term3' + rnDefBody + rnBulletList + rnBulletItem + rnInner + rnLeaf 'point1' + rnBulletItem + rnInner + rnLeaf 'point2' + rnDefBody + rnInner + rnLeaf 'term3definition2' + """) + suite "Warnings": test "warnings for broken footnotes/links/substitutions": let input = dedent""" @@ -778,7 +1254,7 @@ suite "Warnings": lastParagraph """ var warnings = new seq[string] - let output = input.toAst(warnings=warnings) + let output = input.toAst(rstOptions=preferRst, warnings=warnings) check(warnings[] == @[ "input(3, 14) Warning: broken link 'citation-som'", "input(5, 7) Warning: broken link 'a broken Link'", @@ -786,6 +1262,22 @@ suite "Warnings": "input(9, 6) Warning: broken link 'short.link'" ]) + test "Pandoc Markdown concise link warning points to target": + var warnings = new seq[string] + check( + "ref [here][target]".toAst(warnings=warnings) == + dedent""" + rnInner + rnLeaf 'ref' + rnLeaf ' ' + rnPandocRef + rnInner + rnLeaf 'here' + rnInner + rnLeaf 'target' + """) + check warnings[] == @["input(1, 12) Warning: broken link 'target'"] + test "With include directive and blank lines at the beginning": "other.rst".writeFile(dedent""" @@ -804,7 +1296,7 @@ suite "Warnings": rnParagraph rnLeaf 'here' rnLeaf ' ' - rnRef + rnRstRef rnLeaf 'brokenLink' """) removeFile("other.rst") @@ -1163,7 +1655,7 @@ suite "RST inline markup": test "no punctuation in the end of a standalone URI is allowed": check(dedent""" - [see (http://no.org)], end""".toAst == + [see (http://no.org)], end""".toAst(rstOptions = preferRst) == dedent""" rnInner rnLeaf '[' @@ -1211,6 +1703,19 @@ suite "RST inline markup": rnLeaf 'end' """) + test "Markdown-style link can be split to a few lines": + check(dedent""" + is [term-rewriting + macros](manual.html#term-rewriting-macros)""".toAst == + dedent""" + rnInner + rnLeaf 'is' + rnLeaf ' ' + rnHyperlink + rnLeaf 'term-rewriting macros' + rnLeaf 'manual.html#term-rewriting-macros' + """) + test "URL with balanced parentheses (Markdown rule)": # 2 balanced parens, 1 unbalanced: check(dedent""" diff --git a/tests/stdlib/trstgen.nim b/tests/stdlib/trstgen.nim index d91b5615eb3b..b33ee82a8e1e 100644 --- a/tests/stdlib/trstgen.nim +++ b/tests/stdlib/trstgen.nim @@ -8,9 +8,15 @@ import ../../lib/packages/docutils/rstgen import ../../lib/packages/docutils/rst import unittest, strutils, strtabs import std/private/miscdollars +import std/assertions + +const + NoSandboxOpts = {roPreferMarkdown, roSupportMarkdown, roNimFile, roSandboxDisabled} + preferMarkdown = {roPreferMarkdown, roSupportMarkdown, roNimFile} + preferRst = {roSupportMarkdown, roNimFile} proc toHtml(input: string, - rstOptions: RstParseOptions = {roPreferMarkdown, roSupportMarkdown, roNimFile}, + rstOptions: RstParseOptions = preferMarkdown, error: ref string = nil, warnings: ref seq[string] = nil): string = ## If `error` is nil then no errors should be generated. @@ -47,9 +53,6 @@ proc optionListLabel(opt: string): string = opt & "
    " -const - NoSandboxOpts = {roPreferMarkdown, roSupportMarkdown, roNimFile, roSandboxDisabled} - suite "YAML syntax highlighting": test "Basics": @@ -387,7 +390,7 @@ Some chapter ~~~~~ """ - let output9good = input9good.toHtml + let output9good = input9good.toHtml(preferRst) doAssert "

    Level1

    " in output9good doAssert "

    Level2

    " in output9good doAssert "

    Level3

    " in output9good @@ -417,7 +420,7 @@ Some chapter """ var error9Bad = new string - let output9Bad = input9Bad.toHtml(error=error9Bad) + let output9Bad = input9Bad.toHtml(preferRst, error=error9Bad) check(error9Bad[] == "input(15, 1) Error: new section expected (section " & "level inconsistent: underline ~~~~~ unexpectedly found, while " & "the following intermediate section level(s) are missing on " & @@ -534,6 +537,63 @@ Some chapter let output1 = input1.toHtml doAssert output1 == "GC_step" + test "RST anchors/links to headings": + # Currently in TOC mode anchors are modified (for making links from + # the TOC unique) + let inputNoToc = dedent""" + Type relations + ============== + + Convertible relation + -------------------- + + Ref. `Convertible relation`_ + """ + let outputNoToc = inputNoToc.toHtml + check outputNoToc.count("id=\"type-relations\"") == 1 + check outputNoToc.count("id=\"convertible-relation\"") == 1 + check outputNoToc.count("href=\"#convertible-relation\"") == 1 + + let inputTocCases = @[ + dedent""" + .. contents:: + + Type relations + ============== + + Convertible relation + -------------------- + + Ref. `Convertible relation`_ + + Guards and locks + ================ + """, + dedent""" + Ref. `Convertible relation`_ + + .. contents:: + + Type relations + ============== + + Convertible relation + -------------------- + + Guards and locks + ================ + """ + ] + for inputToc in inputTocCases: + let outputToc = inputToc.toHtml + check outputToc.count("id=\"type-relations\"") == 1 + check outputToc.count("id=\"type-relations-convertible-relation\"") == 1 + check outputToc.count("id=\"convertible-relation\">") == 0 + # Besides "Ref.", heading also contains link to itself: + check outputToc.count( + "href=\"#type-relations-convertible-relation\">") == 2 + check outputToc.count("href=\"#convertible-relation\"") == 0 + test "RST links": let input1 = """ Want to learn about `my favorite programming language`_? @@ -550,7 +610,7 @@ context1 context2 """ - let output1 = input1.toHtml + let output1 = input1.toHtml(preferRst) doAssert "[1]") == 1 doAssert output1.count(">[2]") == 2 doAssert "href=\"#footnote-note\"" in output1 @@ -937,7 +1002,7 @@ Test1 Not references[#note]_[1 #]_ [wrong citation]_ and [not&allowed]_. """ - let output2 = input2.toHtml + let output2 = input2.toHtml(preferRst) doAssert output2 == "Not references[#note]_[1 #]_ [wrong citation]_ and [not&allowed]_." # check that auto-symbol footnotes work: @@ -953,7 +1018,7 @@ Test1 And [*]_. """ - let output3 = input3.toHtml + let output3 = input3.toHtml(preferRst) # both references and footnotes. Footnotes have link to themselves. doAssert output3.count("href=\"#footnotesym-1\">[*]") == 2 doAssert output3.count("href=\"#footnotesym-2\">[**]") == 2 @@ -983,7 +1048,7 @@ Test1 Ref. [#note]_ and [#]_ and [#]_. """ - let output4 = input4.toHtml + let output4 = input4.toHtml(preferRst) doAssert ">[-1]" notin output1 let order = @[ "footnote-3", "[3]", "Manual1.", @@ -1008,7 +1073,7 @@ Test1 Ref. [#note]_ """ var error5 = new string - let output5 = input5.toHtml(error=error5) + let output5 = input5.toHtml(preferRst, error=error5) check(error5[] == "input(1, 1) Error: mismatch in number of footnotes " & "and their refs: 1 (lines 2) != 0 (lines ) for auto-numbered " & "footnotes") @@ -1022,7 +1087,7 @@ Test1 Ref. [*]_ """ var error6 = new string - let output6 = input6.toHtml(error=error6) + let output6 = input6.toHtml(preferRst, error=error6) check(error6[] == "input(1, 1) Error: mismatch in number of footnotes " & "and their refs: 1 (lines 3) != 2 (lines 2, 6) for auto-symbol " & "footnotes") @@ -1032,7 +1097,7 @@ Test1 Ref. [some:citation-2020]_. """ - let output7 = input7.toHtml + let output7 = input7.toHtml(preferRst) doAssert output7.count("href=\"#citation-somecoloncitationminus2020\"") == 2 doAssert output7.count("[Some:CITATION-2020]") == 1 doAssert output7.count("[some:citation-2020]") == 1 @@ -1045,7 +1110,7 @@ Test1 Ref. [som]_. """ var warnings8 = new seq[string] - let output8 = input8.toHtml(warnings=warnings8) + let output8 = input8.toHtml(preferRst, warnings=warnings8) check(warnings8[] == @["input(3, 7) Warning: broken link 'citation-som'"]) # check that footnote group does not break parsing of other directives: @@ -1081,7 +1146,7 @@ Test1 .. [Third] Citation. """ - let output10 = input10.toHtml + let output10 = input10.toHtml(preferRst) doAssert output10.count("
    " & "
    ") == 3 doAssert output10.count("
    ") == 3 @@ -1101,7 +1166,7 @@ Test1 .. [#] Body3 .. [2] Body2. """ - let output12 = input12.toHtml + let output12 = input12.toHtml(preferRst) let orderAuto = @[ "#footnoteauto-1", "[1]", "#footnoteauto-2", "[3]", @@ -1180,7 +1245,7 @@ Test1 "input(8, 4) Warning: language 'anotherLang' not supported" ]) check(output == "
    anything
    " & - "

    \nsomeCode\n

    ") + "

    \nsomeCode

    ") test "RST admonitions": # check that all admonitions are implemented @@ -1321,7 +1386,7 @@ Test1 That was a transition. """ let output1 = input1.toHtml( - NoSandboxOpts + preferRst ) doAssert "

    text""" & "\n") test "Field list: body after newline": - let output = dedent """ + let output = dedent""" :field: text1""".toHtml check "` +import std/assertions proc tester[T](x: T) = let test = toSeq(0..4).map(i => newSeq[int]()) diff --git a/tests/stdlib/tstrmiscs.nim b/tests/stdlib/tstrmiscs.nim index 2e9131ff807c..76b14d27aaae 100644 --- a/tests/stdlib/tstrmiscs.nim +++ b/tests/stdlib/tstrmiscs.nim @@ -1,4 +1,5 @@ -import strmisc +import std/strmisc +import std/assertions doAssert expandTabs("\t", 4) == " " diff --git a/tests/stdlib/tstrscans.nim b/tests/stdlib/tstrscans.nim index 24a3c02f721d..e30c86279bd4 100644 --- a/tests/stdlib/tstrscans.nim +++ b/tests/stdlib/tstrscans.nim @@ -2,7 +2,7 @@ discard """ output: "" """ -import strscans, strutils +import std/[strscans, strutils, assertions] block ParsePasswd: proc parsePasswd(content: string): seq[string] = diff --git a/tests/stdlib/tstrset.nim b/tests/stdlib/tstrset.nim index 71a5520c2f41..f0cf5cbe4f46 100644 --- a/tests/stdlib/tstrset.nim +++ b/tests/stdlib/tstrset.nim @@ -3,10 +3,10 @@ type TRadixNodeKind = enum rnLinear, rnFull, rnLeaf PRadixNode = ref TRadixNode - TRadixNode = object {.inheritable.} + TRadixNode {.inheritable.} = object kind: TRadixNodeKind TRadixNodeLinear = object of TRadixNode - len: int8 + len: uint8 keys: array[0..31, char] vals: array[0..31, PRadixNode] TRadixNodeFull = object of TRadixNode @@ -24,7 +24,7 @@ proc search(r: PRadixNode, s: string): PRadixNode = case r.kind of rnLinear: var x = PRadixNodeLinear(r) - for j in 0..ze(x.len)-1: + for j in 0..int(x.len)-1: if x.keys[j] == s[i]: if s[i] == '\0': return r r = x.vals[j] @@ -63,9 +63,9 @@ proc excl*(r: var PRadixNode, s: string) = of rnFull: PRadixNodeFull(x).b['\0'] = nil of rnLinear: var x = PRadixNodeLinear(x) - for i in 0..ze(x.len)-1: + for i in 0..int(x.len)-1: if x.keys[i] == '\0': - swap(x.keys[i], x.keys[ze(x.len)-1]) + swap(x.keys[i], x.keys[int(x.len)-1]) dec(x.len) break diff --git a/tests/stdlib/tstrtabs.nim b/tests/stdlib/tstrtabs.nim index f629c183c351..036287bfda54 100644 --- a/tests/stdlib/tstrtabs.nim +++ b/tests/stdlib/tstrtabs.nim @@ -88,7 +88,7 @@ value1 = value2 ''' """ -import strtabs +import std/[strtabs, assertions, syncio] var tab = newStringTable({"key1": "val1", "key2": "val2"}, modeStyleInsensitive) diff --git a/tests/stdlib/tstrtabs.nims b/tests/stdlib/tstrtabs.nims index c8ed4ac406c7..3563ad0ad6c9 100644 --- a/tests/stdlib/tstrtabs.nims +++ b/tests/stdlib/tstrtabs.nims @@ -1,4 +1,4 @@ -import strtabs +import std/[strtabs, assertions] static: let t = {"name": "John", "city": "Monaco"}.newStringTable diff --git a/tests/stdlib/tstrtabs2.nim b/tests/stdlib/tstrtabs2.nim index cb534f198897..f055b5d33a76 100644 --- a/tests/stdlib/tstrtabs2.nim +++ b/tests/stdlib/tstrtabs2.nim @@ -3,6 +3,7 @@ discard """ """ import std/strtabs +import std/assertions macro m = var t = {"name": "John"}.newStringTable diff --git a/tests/stdlib/tstrutils.nim b/tests/stdlib/tstrutils.nim index 26fc0c7b0a24..32929ef1793d 100644 --- a/tests/stdlib/tstrutils.nim +++ b/tests/stdlib/tstrutils.nim @@ -4,6 +4,7 @@ discard """ import std/strutils from stdtest/testutils import disableVm +import std/assertions # xxx each instance of `disableVm` and `when not defined js:` should eventually be fixed template rejectParse(e) = @@ -232,18 +233,22 @@ template main() = {.pop.} block: # find - doAssert "0123456789ABCDEFGH".find('A') == 10 - doAssert "0123456789ABCDEFGH".find('A', 5) == 10 - doAssert "0123456789ABCDEFGH".find('A', 5, 10) == 10 - doAssert "0123456789ABCDEFGH".find('A', 5, 9) == -1 - doAssert "0123456789ABCDEFGH".find("A") == 10 - doAssert "0123456789ABCDEFGH".find("A", 5) == 10 - doAssert "0123456789ABCDEFGH".find("A", 5, 10) == 10 - doAssert "0123456789ABCDEFGH".find("A", 5, 9) == -1 - doAssert "0123456789ABCDEFGH".find({'A'..'C'}) == 10 - doAssert "0123456789ABCDEFGH".find({'A'..'C'}, 5) == 10 - doAssert "0123456789ABCDEFGH".find({'A'..'C'}, 5, 10) == 10 - doAssert "0123456789ABCDEFGH".find({'A'..'C'}, 5, 9) == -1 + const haystack: string = "0123456789ABCDEFGH" + doAssert haystack.find('A') == 10 + doAssert haystack.find('A', 5) == 10 + doAssert haystack.find('A', 5, 10) == 10 + doAssert haystack.find('A', 5, 9) == -1 + doAssert haystack.find("A") == 10 + doAssert haystack.find("A", 5) == 10 + doAssert haystack.find("A", 5, 10) == 10 + doAssert haystack.find("A", 5, 9) == -1 + doAssert haystack.find({'A'..'C'}) == 10 + doAssert haystack.find({'A'..'C'}, 5) == 10 + doAssert haystack.find({'A'..'C'}, 5, 10) == 10 + doAssert haystack.find({'A'..'C'}, 5, 9) == -1 + doAssert haystack.find('A', 0, 0) == -1 # search limited to the first char + doAssert haystack.find('A', 5, 0) == -1 # last < start + doAssert haystack.find('A', 5, 4) == -1 # last < start block: const haystack: string = "ABCABABABABCAB" @@ -290,16 +295,16 @@ template main() = # when last <= start, searching for non-empty string block: - let last: int = -1 - doAssert "abcd".find("ab", start=0, last=last) == -1 + let last: int = -1 # searching through whole line + doAssert "abcd".find("ab", start=0, last=last) == 0 doAssert "abcd".find("ab", start=1, last=last) == -1 - doAssert "abcd".find("bc", start=1, last=last) == -1 + doAssert "abcd".find("bc", start=1, last=last) == 1 doAssert "abcd".find("bc", start=2, last=last) == -1 block: let last: int = 0 - doAssert "abcd".find("ab", start=0, last=last) == 0 + doAssert "abcd".find("ab", start=0, last=last) == -1 doAssert "abcd".find("ab", start=1, last=last) == -1 - doAssert "abcd".find("bc", start=1, last=last) == 1 + doAssert "abcd".find("bc", start=1, last=last) == -1 doAssert "abcd".find("bc", start=2, last=last) == -1 block: let last: int = 1 @@ -356,19 +361,23 @@ template main() = doAssert "///".rfind("//", start=3) == -1 # searching for empty string - doAssert "".rfind("") == -1 - doAssert "abc".rfind("") == -1 - doAssert "abc".rfind("", start=1) == -1 - doAssert "abc".rfind("", start=2) == -1 - doAssert "abc".rfind("", start=3) == -1 - doAssert "abc".rfind("", start=4) == -1 - doAssert "abc".rfind("", start=400) == -1 - - doAssert "abc".rfind("", start=1, last=3) == -1 - doAssert "abc".rfind("", start=1, last=2) == -1 - doAssert "abc".rfind("", start=1, last=1) == -1 - doAssert "abc".rfind("", start=1, last=0) == -1 - doAssert "abc".rfind("", start=1, last = -1) == -1 + doAssert "".rfind("") == 0 + doAssert "abc".rfind("") == 3 + doAssert "abc".rfind("", start=1) == 3 + doAssert "abc".rfind("", start=2) == 3 + doAssert "abc".rfind("", start=3) == 3 + doAssert "abc".rfind("", start=4) == 4 + doAssert "abc".rfind("", start=400) == 400 + + doAssert "abc".rfind("", start=1, last=3) == 3 + doAssert "abc".rfind("", start=1, last=2) == 2 + doAssert "abc".rfind("", start=1, last=1) == 1 + # This returns the start index instead of the last index + # because start > last + doAssert "abc".rfind("", start=1, last=0) == 1 + doAssert "abc".rfind("", start=1, last = -1) == 3 + + doAssert "abc".rfind("", start=0, last=0) == 0 # when last <= start, searching for non-empty string block: @@ -859,5 +868,10 @@ bar doAssert nimIdentNormalize("Foo_bar") == "Foobar" doAssert nimIdentNormalize("_Foo_bar") == "_foobar" + block: # bug #19500 + doAssert "abc \0 def".find("def") == 6 + doAssert "abc \0 def".find('d') == 6 + + static: main() main() diff --git a/tests/stdlib/tstrutils2.nim b/tests/stdlib/tstrutils2.nim index 881817f90434..22a935ab8148 100644 --- a/tests/stdlib/tstrutils2.nim +++ b/tests/stdlib/tstrutils2.nim @@ -1,10 +1,16 @@ +discard """ + matrix: "--gc:refc; --gc:orc" +""" + import "$lib/.." / compiler/strutils2 +import std/assertions block: # setLen var a = "abc" a.setLen 0 a.setLen 3, isInit = false - doAssert a[1] == 'b' + when defined(gcRefc): # bug #19763 + doAssert a[1] == 'b' a.setLen 0 a.setLen 3, isInit = true doAssert a[1] == '\0' diff --git a/tests/stdlib/tsugar.nim b/tests/stdlib/tsugar.nim index 72abadae77d6..9c1213901f90 100644 --- a/tests/stdlib/tsugar.nim +++ b/tests/stdlib/tsugar.nim @@ -4,6 +4,7 @@ x + y = 30 ''' """ import std/[sugar, algorithm, random, sets, tables, strutils] +import std/[syncio, assertions] template main() = block: # `=>` diff --git a/tests/stdlib/tsums.nim b/tests/stdlib/tsums.nim index 4c29d3e106ab..071e0b303cd2 100644 --- a/tests/stdlib/tsums.nim +++ b/tests/stdlib/tsums.nim @@ -1,5 +1,6 @@ import std/sums from math import pow +import std/assertions var epsilon = 1.0 while 1.0 + epsilon != 1.0: diff --git a/tests/stdlib/tsysrand.nim b/tests/stdlib/tsysrand.nim index c6d43a8fbc06..e6b65e70f1df 100644 --- a/tests/stdlib/tsysrand.nim +++ b/tests/stdlib/tsysrand.nim @@ -4,7 +4,7 @@ discard """ """ import std/sysrand - +import std/assertions template main() = block: diff --git a/tests/stdlib/tsystem.nim b/tests/stdlib/tsystem.nim index 00be16275690..1a976f7a2ea1 100644 --- a/tests/stdlib/tsystem.nim +++ b/tests/stdlib/tsystem.nim @@ -3,6 +3,7 @@ discard """ """ import stdtest/testutils +import std/assertions # TODO: in future work move existing `system` tests here, where they belong @@ -74,3 +75,90 @@ template main = static: main() main() + +# bug #19967 +block: + type + X = object + a: string + b: set[char] + + var y = X(b: {'a'}) + + reset(y) + + doAssert y.b == {} + +block: + type + X = object + a: string + b: int + + var y = X(b: 1314) + + reset(y) + + doAssert y.b == 0 + +block: + type + X = object + a: string + b: float + + var y = X(b: 1314.521) + + reset(y) + + doAssert y.b == 0.0 + +block: + type + X = object + a: string + b: string + + var y = X(b: "1314") + + reset(y) + + doAssert y.b == "" + +block: + type + X = object + a: string + b: seq[int] + + var y = X(b: @[1, 3]) + + reset(y) + + doAssert y.b == @[] + +block: + type + X = object + a: string + b: tuple[a: int, b: string] + + var y = X(b: (1, "cc")) + + reset(y) + + doAssert y.b == (0, "") + +block: + type + Color = enum + Red, Blue, Yellow + X = object + a: string + b: set[Color] + + var y = X(b: {Red, Blue}) + + reset(y) + doAssert y.b == {} + diff --git a/tests/stdlib/ttables.nim b/tests/stdlib/ttables.nim index c1ae89b320ec..ab6502411128 100644 --- a/tests/stdlib/ttables.nim +++ b/tests/stdlib/ttables.nim @@ -1,4 +1,5 @@ import tables, hashes +import std/assertions type Person = object diff --git a/tests/stdlib/ttasks.nim b/tests/stdlib/ttasks.nim index 75fed9f9bd51..4889d49d9445 100644 --- a/tests/stdlib/ttasks.nim +++ b/tests/stdlib/ttasks.nim @@ -1,9 +1,10 @@ discard """ targets: "c cpp" - matrix: "--gc:orc" + matrix: "--gc:orc --threads:off" """ import std/[tasks, strformat] +import std/assertions block: var s = "" diff --git a/tests/stdlib/ttempfiles.nim b/tests/stdlib/ttempfiles.nim index 297410686a6a..1159e08efc6a 100644 --- a/tests/stdlib/ttempfiles.nim +++ b/tests/stdlib/ttempfiles.nim @@ -4,6 +4,7 @@ discard """ import std/tempfiles import std/[os, nre] +import std/[assertions, syncio] const prefix = "D20210502T100442" # safety precaution to only affect files/dirs with this prefix diff --git a/tests/stdlib/tterminal.nim b/tests/stdlib/tterminal.nim index 364c8d82e72b..16365e71c68d 100644 --- a/tests/stdlib/tterminal.nim +++ b/tests/stdlib/tterminal.nim @@ -1,7 +1,6 @@ discard """ action: compile """ - import terminal, colors styledEcho fgColor, colRed, "Test" diff --git a/tests/stdlib/tterminal_12759.nim b/tests/stdlib/tterminal_12759.nim index d6034ab577c2..e9ea3127cdac 100644 --- a/tests/stdlib/tterminal_12759.nim +++ b/tests/stdlib/tterminal_12759.nim @@ -3,6 +3,7 @@ discard """ """ import terminal +import std/syncio proc test() {.raises:[IOError, ValueError].} = setBackgroundColor(stdout, bgRed) diff --git a/tests/stdlib/ttestutils.nim b/tests/stdlib/ttestutils.nim index d24c5b39aa05..0f8bf16cfe06 100644 --- a/tests/stdlib/ttestutils.nim +++ b/tests/stdlib/ttestutils.nim @@ -1,4 +1,5 @@ import stdtest/testutils +import std/assertions block: # assertAll assertAll: diff --git a/tests/stdlib/tthreadpool.nim b/tests/stdlib/tthreadpool.nim index 897c7d173548..bc574faebd9b 100644 --- a/tests/stdlib/tthreadpool.nim +++ b/tests/stdlib/tthreadpool.nim @@ -3,7 +3,7 @@ discard """ disabled: "freebsd" output: "42" """ - +import std/assertions from std/threadpool import spawn, `^`, sync block: # bug #12005 proc doworkok(i: int) {.thread.} = echo i diff --git a/tests/stdlib/ttimes.nim b/tests/stdlib/ttimes.nim index fd440eb2049d..4f396c735e18 100644 --- a/tests/stdlib/ttimes.nim +++ b/tests/stdlib/ttimes.nim @@ -3,6 +3,7 @@ discard """ """ import times, strutils, unittest +import std/assertions when not defined(js): import os diff --git a/tests/stdlib/ttypeinfo.nim b/tests/stdlib/ttypeinfo.nim index 1cddea781467..5e17c151a277 100644 --- a/tests/stdlib/ttypeinfo.nim +++ b/tests/stdlib/ttypeinfo.nim @@ -1,4 +1,5 @@ -import typeinfo +import std/typeinfo +import std/assertions type TE = enum diff --git a/tests/stdlib/ttypetraits.nim b/tests/stdlib/ttypetraits.nim index 799bcf6e270b..574204da656e 100644 --- a/tests/stdlib/ttypetraits.nim +++ b/tests/stdlib/ttypetraits.nim @@ -5,6 +5,7 @@ discard """ # xxx merge with tests/metatype/ttypetraits.nim import std/typetraits +import std/assertions macro testClosure(fn: typed, flag: static bool) = if flag: diff --git a/tests/stdlib/tunicode.nim b/tests/stdlib/tunicode.nim index 3a8206c8f7ef..2b1cb2385ec0 100644 --- a/tests/stdlib/tunicode.nim +++ b/tests/stdlib/tunicode.nim @@ -1,5 +1,5 @@ import std/unicode - +import std/assertions proc asRune(s: static[string]): Rune = ## Compile-time conversion proc for converting string literals to a Rune diff --git a/tests/stdlib/tunidecode.nim b/tests/stdlib/tunidecode.nim index be8e0523c9e9..653016ea966b 100644 --- a/tests/stdlib/tunidecode.nim +++ b/tests/stdlib/tunidecode.nim @@ -5,6 +5,7 @@ discard """ import unidecode import std/unidecode # #14112 +import std/assertions loadUnidecodeTable("lib/pure/unidecode/unidecode.dat") diff --git a/tests/stdlib/tunittest.nim b/tests/stdlib/tunittest.nim index 97a45e199b3f..8aa7f9fad206 100644 --- a/tests/stdlib/tunittest.nim +++ b/tests/stdlib/tunittest.nim @@ -22,7 +22,7 @@ discard """ targets: "c js" """ -import std/[unittest, sequtils] +import std/[unittest, sequtils, assertions] proc doThings(spuds: var int): int = spuds = 24 diff --git a/tests/stdlib/tunittesttemplate.nim b/tests/stdlib/tunittesttemplate.nim index 2ca50a18b3d5..c29e0de01bff 100644 --- a/tests/stdlib/tunittesttemplate.nim +++ b/tests/stdlib/tunittesttemplate.nim @@ -8,9 +8,9 @@ discard """ """ -# bug #6736 -import unittest +# bug #6736 +import std/unittest type A = object diff --git a/tests/stdlib/turi.nim b/tests/stdlib/turi.nim index a3b6afe2c2fb..77ba02dd186a 100644 --- a/tests/stdlib/turi.nim +++ b/tests/stdlib/turi.nim @@ -5,6 +5,7 @@ discard """ import std/uri from std/uri {.all.} as uri2 import removeDotSegments from std/sequtils import toSeq +import std/assertions template main() = block: # encodeUrl, decodeUrl @@ -274,7 +275,9 @@ template main() = doAssert encodeQuery({"foo": ""}) == "foo" doAssert encodeQuery({"foo": ""}, omitEq = false) == "foo=" doAssert encodeQuery({"a": "1", "b": "", "c": "3"}) == "a=1&b&c=3" + doAssert encodeQuery({"a": "1", "b": "", "c": "3"}, sep = ';') == "a=1;b;c=3" doAssert encodeQuery({"a": "1", "b": "", "c": "3"}, omitEq = false) == "a=1&b=&c=3" + doAssert encodeQuery({"a": "1", "b": "", "c": "3"}, omitEq = false, sep = ';') == "a=1;b=;c=3" block: # `?` block: @@ -300,7 +303,9 @@ template main() = block: # decodeQuery doAssert toSeq(decodeQuery("a=1&b=0")) == @[("a", "1"), ("b", "0")] + doAssert toSeq(decodeQuery("a=1;b=0", sep = ';')) == @[("a", "1"), ("b", "0")] doAssert toSeq(decodeQuery("a=1&b=2c=6")) == @[("a", "1"), ("b", "2c=6")] + doAssert toSeq(decodeQuery("a=1;b=2c=6", sep = ';')) == @[("a", "1"), ("b", "2c=6")] block: # bug #17481 let u1 = parseUri("./") diff --git a/tests/stdlib/tuserlocks.nim b/tests/stdlib/tuserlocks.nim index 9251fa9e2762..ba8ea050eaa8 100644 --- a/tests/stdlib/tuserlocks.nim +++ b/tests/stdlib/tuserlocks.nim @@ -3,6 +3,7 @@ discard """ """ import std/rlocks +import std/assertions var r: RLock r.initRLock() diff --git a/tests/stdlib/tvarargs.nim b/tests/stdlib/tvarargs.nim index d56be154bc9a..3207572b5ad7 100644 --- a/tests/stdlib/tvarargs.nim +++ b/tests/stdlib/tvarargs.nim @@ -2,7 +2,7 @@ discard """ targets: "c js" matrix: "--gc:refc; --gc:arc" """ - +import std/assertions template main = proc hello(x: varargs[string]): seq[string] = diff --git a/tests/stdlib/tvarints.nim b/tests/stdlib/tvarints.nim index 3bba4f457bea..bb0d3d37fa93 100644 --- a/tests/stdlib/tvarints.nim +++ b/tests/stdlib/tvarints.nim @@ -1,4 +1,5 @@ import std/varints +import std/assertions # xxx doesn't work with js: tvarints.nim(18, 14) `wrLen == rdLen` [AssertionDefect] diff --git a/tests/stdlib/twchartoutf8.nim b/tests/stdlib/twchartoutf8.nim index a6602e3e3981..add104b07d1a 100644 --- a/tests/stdlib/twchartoutf8.nim +++ b/tests/stdlib/twchartoutf8.nim @@ -2,6 +2,8 @@ discard """ output: '''OK''' """ +import std/[syncio, assertions] + #assume WideCharToMultiByte always produce correct result #windows only @@ -66,8 +68,7 @@ else: #RFC-2781 "UTF-16, an encoding of ISO 10646" - var wc: WideCString - unsafeNew(wc, 1024 * 4 + 2) + var wc: WideCString = newWideCString(1024 * 2) #U+0000 to U+D7FF #skip the U+0000 diff --git a/tests/stdlib/twith.nim b/tests/stdlib/twith.nim index 80382f7c42f7..b2d72bd0cae4 100644 --- a/tests/stdlib/twith.nim +++ b/tests/stdlib/twith.nim @@ -1,4 +1,5 @@ import std/with +import std/[assertions, formatfloat] type Foo = object diff --git a/tests/stdlib/twordwrap.nim b/tests/stdlib/twordwrap.nim index c90dd95814b0..a08e64cf968d 100644 --- a/tests/stdlib/twordwrap.nim +++ b/tests/stdlib/twordwrap.nim @@ -1,4 +1,5 @@ import std/wordwrap +import std/assertions when true: let diff --git a/tests/stdlib/twrapnils.nim b/tests/stdlib/twrapnils.nim index a0549c1bc79a..5d5c1ab2d0f9 100644 --- a/tests/stdlib/twrapnils.nim +++ b/tests/stdlib/twrapnils.nim @@ -1,5 +1,6 @@ import std/wrapnils from std/options import get, isSome +import std/assertions proc checkNotZero(x: float): float = doAssert x != 0 diff --git a/tests/stdlib/txmltree.nim b/tests/stdlib/txmltree.nim index d2f7132690c8..f6b7c62e8e3a 100644 --- a/tests/stdlib/txmltree.nim +++ b/tests/stdlib/txmltree.nim @@ -1,4 +1,4 @@ -import xmltree +import std/[xmltree, assertions] block: diff --git a/tests/stdlib/tyield.nim b/tests/stdlib/tyield.nim index 85be97365119..0cf52999c00c 100644 --- a/tests/stdlib/tyield.nim +++ b/tests/stdlib/tyield.nim @@ -3,6 +3,7 @@ discard """ """ import std/[sugar, algorithm] +import std/assertions block: var x = @[(6.0, 6, '6'), diff --git a/tests/stdlib/uselocks.nim b/tests/stdlib/uselocks.nim index e9d23f9d9ac2..f87623b5e405 100644 --- a/tests/stdlib/uselocks.nim +++ b/tests/stdlib/uselocks.nim @@ -1,4 +1,5 @@ import locks +import std/assertions type MyType* [T] = object lock: Lock diff --git a/tests/strictnotnil/tnilcheck.nim b/tests/strictnotnil/tnilcheck.nim index c2d009b709a7..b8057de748bb 100644 --- a/tests/strictnotnil/tnilcheck.nim +++ b/tests/strictnotnil/tnilcheck.nim @@ -1,6 +1,5 @@ discard """ -cmd: "nim check $file" -action: "reject" +action: compile """ import tables diff --git a/tests/stylecheck/fileinfo.nim b/tests/stylecheck/fileinfo.nim new file mode 100644 index 000000000000..d6faf0c7350e --- /dev/null +++ b/tests/stylecheck/fileinfo.nim @@ -0,0 +1,2 @@ +# fileinfo.nim +type FileInfo* = object \ No newline at end of file diff --git a/tests/stylecheck/t20397.nim b/tests/stylecheck/t20397.nim new file mode 100644 index 000000000000..486a97d73e92 --- /dev/null +++ b/tests/stylecheck/t20397.nim @@ -0,0 +1,4 @@ +{.hintAsError[Name]:on.} +var a_b = 1 +discard a_b +{.hintAsError[Name]:off.} \ No newline at end of file diff --git a/tests/stylecheck/t20397_1.nim b/tests/stylecheck/t20397_1.nim new file mode 100644 index 000000000000..24f5791f8998 --- /dev/null +++ b/tests/stylecheck/t20397_1.nim @@ -0,0 +1,8 @@ +discard """ + matrix: "--styleCheck:off" +""" + +{.hintAsError[Name]:on.} +var a_b = 1 +discard a_b +{.hintAsError[Name]:off.} \ No newline at end of file diff --git a/tests/stylecheck/t20397_2.nim b/tests/stylecheck/t20397_2.nim new file mode 100644 index 000000000000..3b8e1c4d6417 --- /dev/null +++ b/tests/stylecheck/t20397_2.nim @@ -0,0 +1,7 @@ +discard """ + errormsg: "'a_b' should be: 'aB'" + matrix: "--styleCheck:error" +""" + +var a_b = 1 +discard a_b \ No newline at end of file diff --git a/tests/stylecheck/taccept.nim b/tests/stylecheck/taccept.nim index afe02a65c88a..43a9ab71f3c6 100644 --- a/tests/stylecheck/taccept.nim +++ b/tests/stylecheck/taccept.nim @@ -15,3 +15,8 @@ template hello = doAssert iD == "string" hello() + +# bug #12955 +import os +import fileinfo +var xs: seq[fileinfo.FileInfo] diff --git a/tests/system/tdeepcopy.nim b/tests/system/tdeepcopy.nim index 383d2e8d134e..92ba48115b6c 100644 --- a/tests/system/tdeepcopy.nim +++ b/tests/system/tdeepcopy.nim @@ -1,4 +1,5 @@ discard """ + matrix: "--mm:refc; --mm:orc --deepcopy:on" output: "ok" """ diff --git a/tests/system/tgcnone.nim b/tests/system/tgcnone.nim index 700176d5f80d..47c6c601451a 100644 --- a/tests/system/tgcnone.nim +++ b/tests/system/tgcnone.nim @@ -1,5 +1,5 @@ discard """ - matrix: "--gc:none -d:useMalloc" + matrix: "--gc:none -d:useMalloc --threads:off" """ # bug #15617 let x = 4 diff --git a/tests/system/tslimsystem.nim b/tests/system/tslimsystem.nim new file mode 100644 index 000000000000..690f4ee9badf --- /dev/null +++ b/tests/system/tslimsystem.nim @@ -0,0 +1,6 @@ +discard """ + output: "123" + matrix: "-d:nimPreviewSlimSystem" +""" + +echo 123 \ No newline at end of file diff --git a/tests/template/t13515.nim b/tests/template/t13515.nim new file mode 100644 index 000000000000..ffebedcbe8f3 --- /dev/null +++ b/tests/template/t13515.nim @@ -0,0 +1,16 @@ +discard """ + action: compile +""" + +template test: bool = true + +# compiles: +if not test: + echo "wtf" + +# does not compile: +template x = + if not test: + echo "wtf" + +x diff --git a/tests/template/template_various.nim b/tests/template/template_various.nim index e7a2be748df7..6b1290fb9643 100644 --- a/tests/template/template_various.nim +++ b/tests/template/template_various.nim @@ -274,10 +274,10 @@ parse9: block gensym1: template x: untyped = -1 template t1() = - template x: untyped {.gensym.} = 1 + template x: untyped {.gensym, redefine.} = 1 echo x() # 1 template t2() = - template x: untyped = 1 # defaults to {.inject.} + template x: untyped {.redefine.} = 1 # defaults to {.inject.} echo x() # -1 injected x not available during template definition t1() t2() diff --git a/tests/template/tinnerouterproc.nim b/tests/template/tinnerouterproc.nim new file mode 100644 index 000000000000..1f15fb13e0e4 --- /dev/null +++ b/tests/template/tinnerouterproc.nim @@ -0,0 +1,8 @@ +block: # #20002 + proc bar(x: int): int = 10 + template foo = + proc bar(x: int): int {.gensym.} = x + 2 + doAssert bar(3) == 5 + discard 3.bar # evaluates to 10 but only check if it compiles for now + block: + foo() diff --git a/tests/template/tredefinition_override.nim b/tests/template/tredefinition_override.nim new file mode 100644 index 000000000000..0bda4025ba9b --- /dev/null +++ b/tests/template/tredefinition_override.nim @@ -0,0 +1,33 @@ +{.push warningAsError[TemplateRedefinition]: on.} + +doAssert not (compiles do: + template foo(): int = 1 + template foo(): int = 2) +doAssert (compiles do: + template foo(): int = 1 + template foo(): int {.redefine.} = 2) +doAssert not (compiles do: + block: + template foo() = + template bar: string {.gensym.} = "a" + template bar: string {.gensym.} = "b" + foo()) +doAssert (compiles do: + block: + template foo() = + template bar: string {.gensym.} = "a" + template bar: string {.gensym, redefine.} = "b" + foo()) + +block: + template foo(): int = 1 + template foo(): int {.redefine.} = 2 + doAssert foo() == 2 +block: + template foo(): string = + template bar: string {.gensym.} = "a" + template bar: string {.gensym, redefine.} = "b" + bar() + doAssert foo() == "b" + +{.pop.} diff --git a/tests/template/twrongmapit.nim b/tests/template/twrongmapit.nim index 5b8663cf9a23..13f53d1192db 100644 --- a/tests/template/twrongmapit.nim +++ b/tests/template/twrongmapit.nim @@ -1,4 +1,5 @@ discard """ + disabled: true output: "####" """ # unfortunately our tester doesn't support multiple lines of compiler diff --git a/tests/template/utemplates.nim b/tests/template/utemplates.nim index 7674ba7c0226..d70746578688 100644 --- a/tests/template/utemplates.nim +++ b/tests/template/utemplates.nim @@ -22,7 +22,7 @@ block: # templates can be redefined multiple times if not cond: fail(msg) template assertionFailed(body: untyped) {.dirty.} = - template fail(msg: string): typed = + template fail(msg: string): typed {.redefine.} = body assertionFailed: diff --git a/tests/testament/tinlinemsg.nim b/tests/testament/tinlinemsg.nim new file mode 100644 index 000000000000..199c263e998e --- /dev/null +++ b/tests/testament/tinlinemsg.nim @@ -0,0 +1,8 @@ +discard """ + matrix: "--errorMax:0 --styleCheck:error" +""" + +proc generic_proc*[T](a_a: int) = #[tt.Error + ^ 'generic_proc' should be: 'genericProc'; tt.Error + ^ 'a_a' should be: 'aA' ]# + discard diff --git a/tests/testament/tshould_not_work.nim b/tests/testament/tshould_not_work.nim index e11944e8f111..8c99510a0a9d 100644 --- a/tests/testament/tshould_not_work.nim +++ b/tests/testament/tshould_not_work.nim @@ -43,8 +43,7 @@ import stdtest/testutils proc main = const nim = getCurrentCompilerExe() - # TODO: bin/testament instead? like other tools (eg bin/nim, bin/nimsuggest etc) - let testamentExe = "testament/testament" + let testamentExe = "bin/testament" let cmd = fmt"{testamentExe} --directory:testament --colors:off --backendLogging:off --nim:{nim} category shouldfail" let (outp, status) = execCmdEx(cmd) doAssert status == 1, $status diff --git a/tests/testdata/mycert.pem b/tests/testdata/mycert.pem index 69039fe04936..61dcb685c325 100644 --- a/tests/testdata/mycert.pem +++ b/tests/testdata/mycert.pem @@ -1,32 +1,81 @@ -----BEGIN PRIVATE KEY----- -MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAL9QXX/nuiFbizdI -Uhg1D9gG0GIANENvKwdWTlOlZAoOqvjXPFLGh+87yhvkq4f5FcICkSDao2SfeZcP -JsgD7T01owt8x48898+d91i7nIpr6IXGPyBxHOuaxAITY1D+MbbkhIGUVrEqKEOm -qfS9cqPZaDNkx8xVef0HPCmqEme9AgMBAAECgYBxqrQCvJFQJG3QiL2N+GjTdyj0 -MR7cOf6cu2CKPifz+ccHVgpXO/Gj6Cgq7nAjt5B/1rqXhI+zxzSc1bm6+OpIfakS -E0DLCFacECmL0v3c+XLxTtMhFZF5u7Yq0UMsuWmDSfRb4sbRjC+s+c51i5N0485k -b3un/MDI/i/jD/YZGQJBAPLtcuMIwEblUR1uw7NFezXdauXCRFkekoSlJNvpdM/Z -XDRcuWioek5yD8FvMpTz7H2e26Ev645JT5lIuN4Eti8CQQDJm+Qt9NYUohRsU279 -GYI3vXsXKKqmA22at4I3KRXPSeYV1vtQLYWWqGAXzgGkUEVBY0chmHyDcNwkUsNw -svHTAkEAwOTpD/vX6bOXOD7GqKgoULozcqNScE2FXExhuzliJtTakT17f+4fyABs -IFWynXIevBUTIqeRbJcr3HRRTwIAwwJBAJQ8XkL4IaxcG/4mPpY0ek13sZiumwKj -xKQcx869E78tS9LFFlW2kuHafYUjQIvLRZC1aWinUO3oPsUqYW9s82cCQFjoods5 -YsWEJB2RKCT5nhyAXEZLehxF+FXr+JjLMHkuEINKTnHHKjHJ7LbMcTCKUJAcKDTA -qZFEq5N1aT6DrAU= +MIIJQwIBADANBgkqhkiG9w0BAQEFAASCCS0wggkpAgEAAoICAQDgpWhnNdNSUbwJ +39mKf+PA879K1wyT23LC3Ipo5g5vaRjB4vrSh5vzrjK5Q/z42yRDLQ2ojEdUne19 +1xMaM+WqTXnfkf9sou4VJ0HsdQ6jOU1LDRPrPvCjHLZjyMu18sGvQf4FATBwWN3n +QhmvcbleTeyv2pgMNeQDfjuIhRJ/aCIi/WYQ466+Rcj0y3/udYX1yPYf2mszXnSE +i2iWgdsjx0qkDU/nJhnKXhKfucAm9Ds0YfQdZYN3rEzfGkJzZUhDG1n30ghu+vUg +qqLi6HU/HqBxKxBlJf2HdMNK4VLkJuFC+wCI0JfF6VYhcNmtZzlQy3M9WjYvbXUY +nvMkQzUECaSaAtmbhFFfgey1Q3IEQS/j1lsVxtuGUE3YaoyaK22/CkVInDz0aka9 +CKq77x7S/V5PP/wwPV4s8XsbT6/34KemuY3oU84+bf5SBuWdzBY0hnDO9c/FaOnZ +4yjLMQi285HLpK417Z32DmzIkb7GjKtP9WLvsWGgbgquEjcFaIT+erkN09cDjsG/ +URWbKcsQkz8v9E4zj0rxdanXoJEtg1gm+dPsiXokZOLTeaMVtDoTkWeCM/VAp25J +MTc656QRUTSzUODK5tXQ3JgE2XIPGV/PsMi7T/q9xFBQmhUk5kfLNlQ1cy/aiTWK +8pjHcmxbyD121AimuTtv9CFYnYtzyQIDAQABAoICAAMYZFrfs/yzYZrlObMd1f6H +nUAjvGmhIXCr50BQwywnz46EWR5jffOal9pTpH2tT0+ZpFGJNUZmMqqENyAqTOTO +0noRIerWR9+EvfTLHBuFo5oAISEhqeEleSHg12W3ZZHLn/tjq84we0Y/c+kl8P7q +pfM6WNP6Ph0KNTnJU5rrzWScBzb+XB8FCSLOVwHrHqBnV3TS5p07lPFqllNUkLdq +fI3MHSi7LqnKKAmJXqtqvBIZs2pgRrJ0bk64pue+IoPCMbgnbbRRwuTjVQE5YLww +6NcGV+B86IRgSHyUpDa+jmYE3VoFPcIdV/F4A5fPD0wcsYbL4mk+4dkn/4OlZWqV +NZILp2IGejKaKtM1fr7fV2IRUbGUBN/+rX4I0SRnuq6Q4Ipx8VASbpgXQPBo9XTg +SHHsFbEu2DL8BHVgXdOy4PW6jQ0Ux2LhuJk6AQ5nIlFBYA+c8rSlZQXJbEbk1VZu +1i7iSOn/kx3ULMjUfhI/Ddm6rQqtiLbXSubXCzu1HMPT1FG9LUfAbq6EpiVkpAk1 +TqlciBHsyz//mk2RmIEx0Bt+0bX8FFGTIUiGyrp5s4hAHbgQZbXBAUYMRzWxhZ2Q +G0KBXx46bv3hJUb0GOgbNVxcaPnyrXaS/Hafcbx2LXlEtKiwGnC/yKJ7Hmcrt+AQ +RTaqNU1o/bkSYC7vHMZdAoIBAQD79uYPZPv5GLCKPZ64gc3+tbFXNqkmL7qv14yD +Gr3VubRbJe3Fx+T1cS+t9cjgOofhjFnwsDaFRoyWOYqRV/znwFsvvsDhHOLBr1u6 +qWQiF2CT1uMdXR3P6KD8h2DUVNNccxKqqIJNCR5oD/ngnnByWkQobzlsnoIdXgZm +ozBZjGr2XUMO5dJqUxaXZwY3j4I2hk/Ka9uroApyptl+DTVbPHvjk3MzU9QKUNor +vXEtQ8EmM8Oy6v/33HBmNs6cF5fMpgWz6u+B357OTxAfu8B42jZ18OeLrvkHFxzu +phOB1uXvqtQ0tdksSHHWj3IIZRK7GDGudnDEZ23vbCaxH4zbAoIBAQDkPn+5N3px +7UAECWrvT10TD3xKeqMkFhqRA9gmDE4N9AdoN6T6PzD7Tr3gOgGLq1tXjCjBqAlx +ZIDTnih3IK3xoRk2zmhq5+LfM8LQRAxAC8IsoQMXAsmW1KlS6MR70m50pFR8NK6r +UmOdrwVUKp3K6Mecid3LmMVLXGMUKwIJc1k7LJHtwrfi0i5xfBtiqQeaR0lJg4Zr ++zEL/4rHfcq06/P3k0+4uLKZ1LGOvwLPiTA3DADPWZbzUXo7McKOFWF/ycGQBrJq +AJikx15dVLnB16bnHXdxrlrd0LON2R+XfT4+dfRymqZLzrBI3L39t/elTmVYnD3P +punkmZuVwNErAoIBAQD5xOiOPibh6S2n/CmI8XQImIgx0kefSRUhFuV9WVbxtOMq +r9CijONUw1zmb40vahYk6gKGa8fAGg1nJadNKRHVkoNSMx/0h7PpGDIwOZa/jLj6 +FLyS8SmKXiqn6nN8SJI1RQUuE1kHkJCJy7yCg80oLn7+LjOYjxCgmAJ0YDSfsGif +zBebfws0xyTP9RrenO4RqtcR7BWYbk+tE+Tp5aIMzUpqcFJ0gRbjGv8K+QJmQpIH +kqzegcI4LFdnm9D4PxMFlVZ14eCGt+wuy4VKT84efwIZrDN77nmCI9FUaWFRBnxt +NsShc9rS4QWoEg6Sb88/lF47ecGTkIwUGPvJ/WKdAoIBAQClF7/zDPn4Zg+j29wJ +dXJxUwYoKUTP2V0l/43dF5Ft7lFdRMKEcCjR3kbhZZOwnyXW0X65dP4/kt7MMt46 +LN0kpc5DIlHM4iXsJNiJJG9n9BljhqNhhZajDvfbDJrypWdX33Vs0f511YZQjERi +eODh4DZiOCbCGaK7u/u+ns0+YLzuXHLBc9Lmsfj+BTMZzgG9ykpsbkJQ4MS9VP3h +BlAVRYaWUWucxZwKQRqdkfRKgYTqjDgZw0e4f/rVzkxX0YdQk3L65p0up3fB2KOd +BqfGWmJTUbEP/XmkcE0wERkUznazX0aNjucydjJ0wZZ7axIp8+bCjWD4TldoDuPH +Ek05AoIBAECgPfBHLQTAsI+wHbFsu/are28BOiJCSEXjRv88CbUbj/qgAppFuXbx +900WwJ1rVWd5x3LFa3VfyuAqYMi5jzmX9kWgEsC0WhgfyIRFiynw45LlcT4u3fWg +vJEx01lGgFVjnYfFUDS9d1MuiXGxIHrNhzHOP2x2CsS5vrFHav7iwG9YULEk8tJr +My0wzjF3UJ2/5DjGK56WuzauLYKrQ6Faw8dWUy4e/bNYId8wglhQQW548JwJEGmq +nq+EzTfEupXH57Bw7MGEOfdlhv98zNT9VcvBAN09vHeF3Hh6AM4aiGSUIt2HIkto +zvw+fqZ2Sk9O5qva+KE1QMVtY1EICI8= -----END PRIVATE KEY----- -----BEGIN CERTIFICATE----- -MIICgDCCAemgAwIBAgIJANpVfZSDAyNgMA0GCSqGSIb3DQEBBQUAMFkxCzAJBgNV -BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX -aWRnaXRzIFB0eSBMdGQxEjAQBgNVBAMMCWxvY2FsaG9zdDAeFw0xMjA2MDMxMjI4 -MDhaFw0xMzA2MDMxMjI4MDhaMFkxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21l -LVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQxEjAQBgNV -BAMMCWxvY2FsaG9zdDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAv1Bdf+e6 -IVuLN0hSGDUP2AbQYgA0Q28rB1ZOU6VkCg6q+Nc8UsaH7zvKG+Srh/kVwgKRINqj -ZJ95lw8myAPtPTWjC3zHjzz3z533WLucimvohcY/IHEc65rEAhNjUP4xtuSEgZRW -sSooQ6ap9L1yo9loM2THzFV5/Qc8KaoSZ70CAwEAAaNQME4wHQYDVR0OBBYEFF2n -Of61swO+XSNrYb4T02tGx8afMB8GA1UdIwQYMBaAFF2nOf61swO+XSNrYb4T02tG -x8afMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADgYEAEPHofdf4acaph5/e -+BzZGsMfRqdPgwp5sxjFKeTQI1A49VL7ykkb0iLKGfKZtvE8MjMrYjzt20E2bIZj -8eCivT6TbNrVRoACCly/lH9fZfWOG6dBu/85IrTAhSKi8yjbRzmjWUkdrcEJ+ZtV -1cahfFar4l4QwYgqp2pDd6ie+zE= +MIIFCTCCAvGgAwIBAgIUKqDcJ71wiMObIQ5sga2sZItNseowDQYJKoZIhvcNAQEL +BQAwFDESMBAGA1UEAwwJbG9jYWxob3N0MB4XDTIyMDUwNDExMzAwN1oXDTIyMDYw +MzExMzAwN1owFDESMBAGA1UEAwwJbG9jYWxob3N0MIICIjANBgkqhkiG9w0BAQEF +AAOCAg8AMIICCgKCAgEA4KVoZzXTUlG8Cd/Zin/jwPO/StcMk9tywtyKaOYOb2kY +weL60oeb864yuUP8+NskQy0NqIxHVJ3tfdcTGjPlqk1535H/bKLuFSdB7HUOozlN +Sw0T6z7woxy2Y8jLtfLBr0H+BQEwcFjd50IZr3G5Xk3sr9qYDDXkA347iIUSf2gi +Iv1mEOOuvkXI9Mt/7nWF9cj2H9prM150hItoloHbI8dKpA1P5yYZyl4Sn7nAJvQ7 +NGH0HWWDd6xM3xpCc2VIQxtZ99IIbvr1IKqi4uh1Px6gcSsQZSX9h3TDSuFS5Cbh +QvsAiNCXxelWIXDZrWc5UMtzPVo2L211GJ7zJEM1BAmkmgLZm4RRX4HstUNyBEEv +49ZbFcbbhlBN2GqMmittvwpFSJw89GpGvQiqu+8e0v1eTz/8MD1eLPF7G0+v9+Cn +prmN6FPOPm3+UgblncwWNIZwzvXPxWjp2eMoyzEItvORy6SuNe2d9g5syJG+xoyr +T/Vi77FhoG4KrhI3BWiE/nq5DdPXA47Bv1EVmynLEJM/L/ROM49K8XWp16CRLYNY +JvnT7Il6JGTi03mjFbQ6E5FngjP1QKduSTE3OuekEVE0s1DgyubV0NyYBNlyDxlf +z7DIu0/6vcRQUJoVJOZHyzZUNXMv2ok1ivKYx3JsW8g9dtQIprk7b/QhWJ2Lc8kC +AwEAAaNTMFEwHQYDVR0OBBYEFEZcUeqH6MfIzC56BlD3NSs2mCgGMB8GA1UdIwQY +MBaAFEZcUeqH6MfIzC56BlD3NSs2mCgGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZI +hvcNAQELBQADggIBADGc7WONP6I6Trb7ici9fQ9qT3wh/RGDcmUDmDtARn9SFtOF +hsbszOMZg1Flj10fuD6OYDonKz4rv+Ieo5VkAYXxxd3J+bx2x1pqd1YSIsvugTwv +pnx39uBR9cjOmt4W7RyzhFnXoVfuSBE6LpkBUjcrqi5xwrQ31mOSCPwe8uDZYEWS +pX49MiHXGTyjQ481QLiOtTBZJa5igfnHUJbJbyZMa86zBQ/clS7+OeDwkvaEpjov +2VQf3QouVLghfLZYWSxWdEKD9+IWHn8rV6qksEb/Ogu4ZtzDRGqJow4j0DeSSEu7 +ns1YeT2mVTFwHjGXCWS+0iE885NDVX/b5YptlwH5PW7aqeXyCS9Hrd1C1GnXoXGp +NHltYRTyNWm974xWg7eu2gbbB8Ng02chXysdkBq7l+7OyA0a2EfX3Cbz3/49+Mqn +viqwNO5toSHVCdfV9Jd0p0CcqryYgyt2YNpJB+2nUQpiW4jviAs49PZg2PpCVw/2 +0cqtaPeUh26Si8UzDOuT697PIuGkZ9Q9QVwccVXtCyA0UpJ13P0fMrA+yEMhtwSs +k1tRm0pUQa6t3v26/cAy+kMhviHBJFwi5dx+y3OMvqQqpQJrgfZawm/o2ZQHy1KP +8m4ngrJzb13evKf216qCwllmQo6Ts4yeI1Ddx8UpdX7RUWpD8Uw4zSi7Th4r -----END CERTIFICATE----- diff --git a/tests/threads/tjsthreads.nim b/tests/threads/tjsthreads.nim index 1085d9157975..2a8ff60fb860 100644 --- a/tests/threads/tjsthreads.nim +++ b/tests/threads/tjsthreads.nim @@ -1,6 +1,6 @@ discard """ targets: "c cpp js" - matrix: "--threads" + matrix: "--threads:on" """ echo 123 diff --git a/tests/threads/tonthreadcreation.nim b/tests/threads/tonthreadcreation.nim index f588a21c90f2..0652d79f873d 100644 --- a/tests/threads/tonthreadcreation.nim +++ b/tests/threads/tonthreadcreation.nim @@ -1,4 +1,5 @@ discard """ + matrix: "--mm:refc; --mm:orc --deepcopy:on" output: '''some string here dying some string here''' """ diff --git a/tests/tools/tnimgrep.nim b/tests/tools/tnimgrep.nim new file mode 100644 index 000000000000..e97b979f18a5 --- /dev/null +++ b/tests/tools/tnimgrep.nim @@ -0,0 +1,402 @@ +discard """ + output: ''' + +[Suite] nimgrep filesystem + +[Suite] nimgrep contents filtering +''' +""" +## Authors: quantimnot, a-mr + +import osproc, os, streams, unittest, strutils + +#======= +# setup +#======= + +var process: Process +var ngStdOut, ngStdErr: string +var ngExitCode: int +let previousDir = getCurrentDir() +let tempDir = getTempDir() +let testFilesRoot = tempDir / "nimgrep_test_files" + +template nimgrep(optsAndArgs): untyped = + process = startProcess(previousDir / "bin/nimgrep " & optsAndArgs, + options = {poEvalCommand}) + ngExitCode = process.waitForExit + ngStdOut = process.outputStream.readAll + ngStdErr = process.errorStream.readAll + +func fixSlash(s: string): string = + if DirSep == '/': + result = s + else: # on Windows + result = s.replace('/', DirSep) + +func initString(len = 1000, val = ' '): string = + result = newString(len) + for i in 0.." - line: 6 + line: 7 """ # bug #3079, #1146 -echo repr(int) +echo repr(int) \ No newline at end of file diff --git a/tests/typerel/ttypedesc_as_genericparam1_orc.nim b/tests/typerel/ttypedesc_as_genericparam1_orc.nim new file mode 100644 index 000000000000..0ee4d8f92563 --- /dev/null +++ b/tests/typerel/ttypedesc_as_genericparam1_orc.nim @@ -0,0 +1 @@ +doAssert repr(int) == "int" \ No newline at end of file diff --git a/tests/typerel/ttypedesc_as_genericparam2.nim b/tests/typerel/ttypedesc_as_genericparam2.nim index ea06606f93bd..882f66584da6 100644 --- a/tests/typerel/ttypedesc_as_genericparam2.nim +++ b/tests/typerel/ttypedesc_as_genericparam2.nim @@ -1,6 +1,7 @@ discard """ + matrix: "--mm:refc" errormsg: "'repr' doesn't support 'void' type" - line: 9 + line: 10 """ # bug #2879 diff --git a/tests/types/tfinalobj.nim b/tests/types/tfinalobj.nim index 94f2e4f0ec78..ad3085132792 100644 --- a/tests/types/tfinalobj.nim +++ b/tests/types/tfinalobj.nim @@ -3,7 +3,7 @@ discard """ """ type - TA = object {.pure, final.} + TA {.pure, final.} = object x: string var diff --git a/tests/types/tillegaltyperecursion.nim b/tests/types/tillegaltyperecursion.nim index 4c53a8b0ee66..372615c4dedc 100644 --- a/tests/types/tillegaltyperecursion.nim +++ b/tests/types/tillegaltyperecursion.nim @@ -1,66 +1,17 @@ discard """ cmd: "nim $target --threads:on $options $file" errormsg: "illegal recursion in type 'TIRC'" - line: 16 + line: 12 """ -import events import net import strutils import os type - TMessageReceivedEventArgs = object of EventArgs - Nick*: string - Message*: string TIRC = object - EventEmitter: EventEmitter - MessageReceivedHandler*: EventHandler Socket: Socket Thread: Thread[TIRC] proc initIRC*(): TIRC = result.Socket = socket() - result.EventEmitter = initEventEmitter() - result.MessageReceivedHandler = initEventHandler("MessageReceived") - -proc IsConnected*(irc: var TIRC): bool = - return running(irc.Thread) - - -proc sendRaw*(irc: var TIRC, message: string) = - irc.Socket.send(message & "\r\L") -proc handleData(irc: TIRC) {.thread.} = - var connected = False - while connected: - var tup = @[irc.Socket] - var o = select(tup, 200) - echo($o) - echo($len(tup)) - if len(tup) == 1: - #Connected - connected = True - - #Parse data here - - else: - #Disconnected - connected = False - return - -proc Connect*(irc: var TIRC, nick: string, host: string, port: int = 6667) = - connect(irc.Socket, host, TPort(port), TDomain.AF_INET) - send(irc.Socket,"USER " & nick & " " & nick & " " & nick & " " & nick & "\r\L") - send(irc.Socket,"NICK " & nick & "\r\L") - var thread: Thread[TIRC] - createThread(thread, handleData, irc) - irc.Thread = thread - - - - -when true: - var irc = initIRC() - irc.Connect("AmryBot[Nim]","irc.freenode.net",6667) - irc.sendRaw("JOIN #nim") - os.Sleep(4000) diff --git a/tests/types/tinheritref.nim b/tests/types/tinheritref.nim index 01f2307c06de..b79926356284 100644 --- a/tests/types/tinheritref.nim +++ b/tests/types/tinheritref.nim @@ -11,11 +11,11 @@ ob = T[int](elem: 23) doAssert ob.elem == 23 type - TTreeIteratorA* = ref object {.inheritable.} + TTreeIteratorA* {.inheritable.} = ref object TKeysIteratorA* = ref object of TTreeIteratorA #compiles - TTreeIterator* [T,D] = ref object {.inheritable.} + TTreeIterator* [T,D] {.inheritable.} = ref object TKeysIterator* [T,D] = ref object of TTreeIterator[T,D] #this not diff --git a/tests/types/told_pragma_syntax1.nim b/tests/types/told_pragma_syntax1.nim new file mode 100644 index 000000000000..49823662d990 --- /dev/null +++ b/tests/types/told_pragma_syntax1.nim @@ -0,0 +1,5 @@ +discard """ + errormsg: "invalid indentation" +""" + +type Foo = object {.final.} diff --git a/tests/types/told_pragma_syntax2.nim b/tests/types/told_pragma_syntax2.nim new file mode 100644 index 000000000000..eea18572541a --- /dev/null +++ b/tests/types/told_pragma_syntax2.nim @@ -0,0 +1,5 @@ +discard """ + errormsg: "invalid indentation" +""" + +type Bar {.final.} [T] = object diff --git a/tests/types/ttopdowninference.nim b/tests/types/ttopdowninference.nim new file mode 100644 index 000000000000..296729cd83d6 --- /dev/null +++ b/tests/types/ttopdowninference.nim @@ -0,0 +1,195 @@ +block: + var s: seq[string] = (discard; @[]) + + var x: set[char] = + if true: + try: + case 1 + of 1: + if false: + {'4'} + else: + block: + s.add "a" + {} + else: {'3'} + except: {'2'} + else: {'1'} + doAssert x is set[char] + doAssert x == {} + doAssert s == @["a"] + + x = {'a', 'b'} + doAssert x == {'a', 'b'} + + x = (s.add "b"; {}) + doAssert x == {} + doAssert s == @["a", "b"] + + let x2: set[byte] = {1} + doAssert x2 == {1u8} + +block: + let x3: array[0..2, byte] = [1, 2, 3] + #let x4: openarray[byte] = [1, 2, 3] + #let x5: openarray[byte] = @[1, 2, 3] + let x6: seq[byte] = @[1, 2, 3] + let x7: seq[seq[float32]] = @[@[1, 2, 3], @[4.3, 5, 6]] + type ABC = enum a, b, c + let x8: array[ABC, byte] = [1, 2, 3] + doAssert x8[a] == 1 + doAssert x8[a] + x8[b] == x8[c] + + const x9: array[-2..2, float] = [0, 1, 2, 3, 4] + let x10: array[ABC, byte] = block: + {.gcsafe.}: + [a: 1, b: 2, c: 3] + proc `@`(x: float): float = x + 1 + doAssert @1 == 2 + let x11: seq[byte] = system.`@`([1, 2, 3]) + +block: + type Foo = object + x: BiggestInt + var foo: Foo + foo.x = case true + of true: ord(1) + else: 0 + foo.x = if true: ord(1) else: 0 + +block: + type Foo = object + x: (float, seq[(byte, seq[byte])]) + + let foo = Foo(x: (1, @{2: @[], 3: @[4, 5]})) + doAssert foo.x == (1.0, @{2u8: @[], 3u8: @[4u8, 5]}) + +block: + type Foo = object + x: tuple[a: float, b: seq[(byte, seq[byte])]] + + let foo = Foo(x: (a: 1, b: @{2: @[3, 4], 5: @[]})) + doAssert foo.x == (1.0, @{2u8: @[3u8, 4], 5u8: @[]}) + +block: + proc foo(): seq[float] = @[1] + + let fooLamb = proc(): seq[float] = @[1] + + doAssert foo() == fooLamb() + +block: + type Foo[T] = float32 + + let x: seq[Foo[int32]] = @[1] + +block: + type Foo = ref object + type Bar[T] = ptr object + + let x1: seq[Foo] = @[nil] + let x2: seq[Bar[int]] = @[nil] + let x3: seq[cstring] = @[nil] + +block: + let x: seq[cstring] = @["abc", nil, "def"] + doAssert x.len == 3 + doAssert x[0] == cstring"abc" + doAssert x[1].isNil + doAssert x[2] == "def".cstring + +block: + type Foo = object + x: tuple[a: float, b: seq[(byte, seq[cstring])]] + + let foo = Foo(x: (a: 1, b: @{2: @[nil, "abc"]})) + doAssert foo.x == (1.0, @{2u8: @[cstring nil, cstring "abc"]}) + +block: + type Foo = object + x: tuple[a: float, b: seq[(byte, seq[ptr int])]] + + let foo = Foo(x: (a: 1, b: @{2: @[nil, nil]})) + doAssert foo.x == (1.0, @{2u8: @[(ptr int)(nil), nil]}) + +when false: # unsupported + block: # type conversion + let x = seq[(cstring, float32)](@{"abc": 1.0, "def": 2.0}) + doAssert x[0] == (cstring"abc", 1.0'f32) + doAssert x[1] == (cstring"def", 2.0'f32) + +block: # enum + type Foo {.pure.} = enum a + type Bar {.pure.} = enum a, b, c + + var s: seq[Bar] = @[a, b, c] + +block: # overload selection + proc foo(x, y: int): int = x + y + 1 + proc foo(x: int): int = x - 1 + var s: seq[proc (x, y: int): int] = @[nil, foo, foo] + var s2: seq[int] + for a in s: + if not a.isNil: s2.add(a(1, 2)) + doAssert s2 == @[4, 4] + +block: # with generics? + proc foo(x, y: int): int = x + y + 1 + proc foo(x: int): int = x - 1 + proc bar[T](x, y: T): T = x - y + var s: seq[proc (x, y: int): int] = @[nil, foo, foo, bar] + var s2: seq[int] + for a in s: + if not a.isNil: s2.add(a(1, 2)) + doAssert s2 == @[4, 4, -1] + proc foo(x, y: float): float = x + y + 1.0 + var s3: seq[proc (x, y: float): float] = @[nil, foo, foo, bar] + var s4: seq[float] + for a in s3: + if not a.isNil: s4.add(a(1, 2)) + doAssert s4 == @[4.0, 4, -1] + +block: # range types + block: + let x: set[range[1u8..5u8]] = {1, 3} + doAssert x == {range[1u8..5u8](1), 3} + doAssert $x == "{1, 3}" + block: + let x: seq[set[range[1u8..5u8]]] = @[{1, 3}] + doAssert x == @[{range[1u8..5u8](1), 3}] + doAssert $x[0] == "{1, 3}" + block: + let x: seq[range[1u8..5u8]] = @[1, 3] + doAssert x == @[range[1u8..5u8](1), 3] + doAssert $x == "@[1, 3]" + block: # already worked before, make sure it still works + let x: set[range['a'..'e']] = {'a', 'c'} + doAssert x == {range['a'..'e']('a'), 'c'} + doAssert $x == "{'a', 'c'}" + block: # extended + let x: seq[set[range['a'..'e']]] = @[{'a', 'c'}] + doAssert x[0] == {range['a'..'e']('a'), 'c'} + doAssert $x == "@[{'a', 'c'}]" + block: + type Foo = object + x: (range[1u8..5u8], seq[(range[1f32..5f32], seq[range['a'..'e']])]) + + let foo = Foo(x: (1, @{2: @[], 3: @['c', 'd']})) + doAssert foo.x == (range[1u8..5u8](1u8), @{range[1f32..5f32](2f32): @[], 3f32: @[range['a'..'e']('c'), 'd']}) + block: + type Foo = object + x: (range[1u8..5u8], seq[(range[1f32..5f32], seq[set[range['a'..'e']]])]) + + let foo = Foo(x: (1, @{2: @[], 3: @[{'c', 'd'}]})) + doAssert foo.x == (range[1u8..5u8](1u8), @{range[1f32..5f32](2f32): @[], 3f32: @[{range['a'..'e']('c'), 'd'}]}) + +block: # templates + template foo: untyped = (1, 2, "abc") + let x: (float, byte, cstring) = foo() + doAssert x[0] == float(1) + doAssert x[1] == byte(2) + doAssert x[2] == cstring("abc") + let (a, b, c) = x + doAssert a == float(1) + doAssert b == byte(2) + doAssert c == cstring("abc") diff --git a/tests/untestable/thttpclient_ssl_remotenetwork.nim b/tests/untestable/thttpclient_ssl_remotenetwork.nim index cbb86dc137da..d2366d9a9754 100644 --- a/tests/untestable/thttpclient_ssl_remotenetwork.nim +++ b/tests/untestable/thttpclient_ssl_remotenetwork.nim @@ -49,8 +49,8 @@ when enableRemoteNetworking and (defined(nimTestsEnableFlaky) or not defined(win ("https://sha512.badssl.com/", bad, "sha512"), ("https://1000-sans.badssl.com/", bad, "1000-sans"), ("https://10000-sans.badssl.com/", good_broken, "10000-sans"), - ("https://ecc256.badssl.com/", good, "ecc256"), - ("https://ecc384.badssl.com/", good, "ecc384"), + ("https://ecc256.badssl.com/", good_broken, "ecc256"), + ("https://ecc384.badssl.com/", good_broken, "ecc384"), ("https://rsa2048.badssl.com/", good, "rsa2048"), ("https://rsa8192.badssl.com/", dubious_broken, "rsa8192"), ("http://http.badssl.com/", good, "regular http"), diff --git a/tests/usingstmt/tthis.nim b/tests/usingstmt/tthis.nim deleted file mode 100644 index 83d75d08c768..000000000000 --- a/tests/usingstmt/tthis.nim +++ /dev/null @@ -1,15 +0,0 @@ - -# bug #4177 - -type - Parent = object of RootObj - parentField: int - Child = object of Parent - childField: int - -{.this: self.} -proc sumFields(self: Child): int = - result = parentField + childField # Error: undeclared identifier: 'parentField' - -proc sumFieldsWorks(self: Child): int = - result = self.parentField + childField diff --git a/tests/usingstmt/tusingstmt.nim b/tests/usingstmt/tusingstmt.nim new file mode 100644 index 000000000000..11803878ef52 --- /dev/null +++ b/tests/usingstmt/tusingstmt.nim @@ -0,0 +1,16 @@ +type + Foo = object + +using + c: Foo + x, y: int + +proc usesSig(c) = discard + +proc foobar(c, y) = discard + +usesSig(Foo()) +foobar(Foo(), 123) +doAssert not compiles(usesSig(123)) +doAssert not compiles(foobar(Foo(), Foo())) +doAssert not compiles(foobar(123, 123)) diff --git a/tests/views/t19986.nim b/tests/views/t19986.nim new file mode 100644 index 000000000000..85a7cf97df65 --- /dev/null +++ b/tests/views/t19986.nim @@ -0,0 +1,42 @@ +discard """ + cmd: '''nim check --hints:off $file''' + action: reject +nimout: ''' +t19986.nim(19, 7) Error: 'foo' borrows from the immutable location 'a' and attempts to mutate it +t19986.nim(28, 7) Error: 'foo' borrows from the immutable location 'a' and attempts to mutate it +t19986.nim(37, 7) Error: 'foo' borrows from the immutable location 'a' and attempts to mutate it +''' +""" + +{.experimental: "views".} + +type + Object = object + id: int + +proc foo() = + let a = Object(id: 3) + var foo: var Object = a + + foo.id = 777 + echo a + +foo() + +proc bar() = + let a = "123" + var foo: var string = a + + foo[0] = '7' + echo a + +bar() + +proc main() = + let a = 3 + var foo: var int = a + + foo = 777 + echo a + +main() diff --git a/tests/views/tdont_mutate.nim b/tests/views/tdont_mutate.nim index 43acaaf717e0..eb5a82cbfbe9 100644 --- a/tests/views/tdont_mutate.nim +++ b/tests/views/tdont_mutate.nim @@ -10,7 +10,7 @@ const Whitespace = {' ', '\t', '\n', '\r'} proc split*(s: string, seps: set[char] = Whitespace, maxsplit: int = -1): Table[int, openArray[char]] #[tt.Error - 'result' borrows from the immutable location 's' and attempts to mutate it +^ 'result' borrows from the immutable location 's' and attempts to mutate it ]# = var last = 0 var splits = maxsplit @@ -35,7 +35,7 @@ proc `$`(x: openArray[char]): string = proc otherTest(x: int) = var y: var int = x #[tt.Error - 'y' borrows from the immutable location 'x' and attempts to mutate it + ^ 'y' borrows from the immutable location 'x' and attempts to mutate it ]# y = 3 diff --git a/tests/views/tviews1.nim b/tests/views/tviews1.nim index 249058eb6c21..b81b17f30bf3 100644 --- a/tests/views/tviews1.nim +++ b/tests/views/tviews1.nim @@ -66,3 +66,14 @@ proc mainB = assert foo.x.y == @[1, 2, 3] mainB() + + +# bug #15897 +type Outer = ref object + value: int +type Inner = object + owner: var Outer + +var o = Outer(value: 1234) +var v = Inner(owner: o).owner.value +doAssert v == 1234 diff --git a/tests/vm/t19199.nim b/tests/vm/t19199.nim new file mode 100644 index 000000000000..6ae48cb5459d --- /dev/null +++ b/tests/vm/t19199.nim @@ -0,0 +1,6 @@ +# bug #19199 +proc mikasa(x: float) = doAssert x == 42 + +static: + mikasa 42.uint.float +mikasa 42.uint.float diff --git a/tests/vm/tfile_rw.nim b/tests/vm/tfile_rw.nim index 8d7a2ca95ddf..439886e497bd 100644 --- a/tests/vm/tfile_rw.nim +++ b/tests/vm/tfile_rw.nim @@ -1,7 +1,3 @@ -discard """ - output: '''ok''' -""" - # test file read write in vm import os, strutils @@ -24,4 +20,3 @@ static: removeFile(filename) -echo "ok" \ No newline at end of file diff --git a/tests/vm/tissues.nim b/tests/vm/tissues.nim index 1cf3afc0021f..f0ae6c296ecc 100644 --- a/tests/vm/tissues.nim +++ b/tests/vm/tissues.nim @@ -1,6 +1,6 @@ import macros -block t9043: # issue #9043 +block t9043: # bug #9043 proc foo[N: static[int]](dims: array[N, int]): string = const N1 = N const N2 = dims.len @@ -26,3 +26,51 @@ block t4952: let tree = newTree(nnkExprColonExpr) let t = (n: tree) doAssert: t.n.kind == tree.kind + + +# bug #19909 +type + SinglyLinkedList[T] = ref object + SinglyLinkedListObj[T] = ref object + + +proc addMoved[T](a, b: var SinglyLinkedList[T]) = + if a.addr != b.addr: discard + +proc addMoved[T](a, b: var SinglyLinkedListObj[T]) = + if a.addr != b.addr: discard + +proc main = + var a: SinglyLinkedList[int]; new a + var b: SinglyLinkedList[int]; new b + a.addMoved b + + var a0: SinglyLinkedListObj[int] + var b0: SinglyLinkedListObj[int] + a0.addMoved b0 + +static: main() + + +# bug #18641 + +type A = object + ha1: int +static: + var a = A() + var a2 = a.addr + a2.ha1 = 11 + doAssert a2.ha1 == 11 + a.ha1 = 12 + doAssert a.ha1 == 12 + doAssert a2.ha1 == 12 # ok +static: + proc fn() = + var a = A() + var a2 = a.addr + a2.ha1 = 11 + doAssert a2.ha1 == 11 + a.ha1 = 12 + doAssert a.ha1 == 12 + doAssert a2.ha1 == 12 # fails + fn() diff --git a/tests/vm/tmisc_vm.nim b/tests/vm/tmisc_vm.nim index bbf618622d4a..af09e8871354 100644 --- a/tests/vm/tmisc_vm.nim +++ b/tests/vm/tmisc_vm.nim @@ -1,7 +1,12 @@ discard """ + targets: "c js" output: ''' [127, 127, 0, 255][127, 127, 0, 255] (data: 1) +(2, 1) +(2, 1) +(2, 1) +(f0: 5) ''' nimout: '''caught Exception @@ -17,6 +22,19 @@ foo4 (a: 0, b: 0) (a: 0, b: 0) (a: 0, b: 0) +z1 m: (lo: 12) +z2 a: (lo: 3) +x1 a: (lo: 3) +x2 a: (lo: 6) +x3 a: (lo: 0) +z3 a: (lo: 3) +x1 a: (lo: 3) +x2 a: (lo: 6) +x3 a: (lo: 0) +(2, 1) +(2, 1) +(2, 1) +(f0: 5) ''' """ import std/sets @@ -249,3 +267,181 @@ static: echo x2[] let x3 = new(ref MyObject) # cannot generate VM code for ref MyObject echo x3[] + +# bug #19464 +type + Wrapper = object + inner: int + +proc assign(r: var Wrapper, a: Wrapper) = + r = a + +proc myEcho(a: Wrapper) = + var tmp = a + assign(tmp, Wrapper(inner: 0)) # this shouldn't modify `a` + doAssert a.inner == 1 + +static: + var result: Wrapper + assign(result, Wrapper(inner: 1)) + myEcho(result) + +when true: + # bug #15974 + type Foo = object + f0: int + + proc fn(a: var Foo) = + var s: Foo + a = Foo(f0: 2) + s = a + doAssert s.f0 == 2 + a = Foo(f0: 3) + doAssert s.f0 == 2 + + proc test2()= + var a = Foo(f0: 1) + fn(a) + + static: test2() + test2() + +# bug #12551 +type + StUint = object + lo: uint64 + +func `+=`(x: var Stuint, y: Stuint) = + x.lo += y.lo + +func `-`(x, y: Stuint): Stuint = + result.lo = x.lo - y.lo + +func `+`(x, y: Stuint): Stuint = + result.lo = x.lo + y.lo + +func `-=`(x: var Stuint, y: Stuint) = + x = x - y + +func `<`(x, y: Stuint): bool= + x.lo < y.lo + +func `==`(x, y: Stuint): bool = + x.lo == y.lo + +func `<=`(x, y: Stuint): bool = + x.lo <= y.lo + +proc div3n2n(r: var Stuint, b: Stuint) = + var d: Stuint + r = d + r += b + +func div2n1n(r: var Stuint, b: Stuint) = + div3n2n(r, b) + +func divmodBZ(x, y: Stuint, r: var Stuint)= + div2n1n(r, y) + r.lo = 3 + +func `mod`(x, y: Stuint): Stuint = + divmodBZ(x, y, result) + +func doublemod_internal(a, m: Stuint): Stuint = + result = a + if a >= m - a: + result -= m + result += a + +func mulmod_internal(a, b, m: Stuint): Stuint = + var (a, b) = (a, b) + swap(a, b) + debugEcho "x1 a: ", a + a = doublemod_internal(a, m) + debugEcho "x2 a: ", a + a = doublemod_internal(a, m) + debugEcho "x3 a: ", a + +func powmod_internal(a, m: Stuint): Stuint = + var a = a + debugEcho "z1 m: ", m + debugEcho "z2 a: ", a + result = mulmod_internal(result, a, m) + debugEcho "z3 a: ", a + a = mulmod_internal(a, a, m) + +func powmod*(a, m: Stuint) = + discard powmod_internal(a mod m, m) + +static: + var x = Stuint(lo: high(uint64)) + var y = Stuint(lo: 12) + + powmod(x, y) + +# bug #16780 +when true: + template swap*[T](a, b: var T) = + var a2 = addr(a) + var b2 = addr(b) + var aOld = a2[] + a2[] = b2[] + b2[] = aOld + + proc rather = + block: + var a = 1 + var b = 2 + swap(a, b) + echo (a,b) + + block: + type Foo = ref object + x: int + var a = Foo(x:1) + var b = Foo(x:2) + swap(a, b) + echo (a.x, b.x) + + block: + type Foo = object + x: int + var a = Foo(x:1) + var b = Foo(x:2) + swap(a, b) + echo (a.x,b.x) + + static: rather() + rather() + +# bug #16020 +when true: + block: + type Foo = object + f0: int + proc main= + var f = Foo(f0: 3) + var f2 = f.addr + f2[].f0 += 1 + f2.f0 += 1 + echo f + static: main() + main() + +import tables, strutils + +# bug #14553 +const PpcPatterns = @[("aaaa", "bbbb"), ("aaaaa", "bbbbb"), ("aaaaaa", "bbbbbb"), ("aaaaaaa", "bbbbbbb"), ("aaaaaaaa", "bbbbb")] + +static: + var + needSecondIdentifier = initTable[uint32, seq[(string, string)]]() + + for (name, pattern) in PpcPatterns: + let + firstPart = 0'u32 + lastPart = "test" + + needSecondIdentifier.mgetOrPut(firstPart, @[]).add((name, pattern)) + + doAssert needSecondIdentifier[0] == @[("aaaa", "bbbb"), ("aaaaa", "bbbbb"), ("aaaaaa", "bbbbbb"), ("aaaaaaa", "bbbbbbb"), ("aaaaaaaa", "bbbbb")] diff --git a/tests/vm/tnewseqofcap.nim b/tests/vm/tnewseqofcap.nim index c61c3c485f1e..a7dc07aa676d 100644 --- a/tests/vm/tnewseqofcap.nim +++ b/tests/vm/tnewseqofcap.nim @@ -1,8 +1,3 @@ -discard """ - output: '''@["aaa", "bbb", "ccc"]''' -""" - - const foo = @["aaa", "bbb", "ccc"] @@ -16,4 +11,4 @@ proc myTuple: tuple[n: int, bar: seq[string]] = const (n, bar) = myTuple() -echo bar \ No newline at end of file +doAssert bar == @["aaa", "bbb", "ccc"] \ No newline at end of file diff --git a/tests/vm/tquadplus.nim b/tests/vm/tquadplus.nim index 552e8fef72f4..acf20cd96723 100644 --- a/tests/vm/tquadplus.nim +++ b/tests/vm/tquadplus.nim @@ -1,8 +1,5 @@ # bug #1023 -discard """ - output: "1 == 1" -""" type Quadruple = tuple[a, b, c, d: int] @@ -14,4 +11,4 @@ const B = (a: 0, b: -2, c: 1, d: 0) C = A + B -echo C.d, " == ", (A+B).d +doAssert $C.d & " == " & $(A+B).d == "1 == 1" diff --git a/tests/vm/treset.nim b/tests/vm/treset.nim index ff8bc5b835f8..32fbc7f04dd4 100644 --- a/tests/vm/treset.nim +++ b/tests/vm/treset.nim @@ -1,6 +1,3 @@ -discard """ - output: '''0''' -""" static: type Obj = object field: int @@ -48,6 +45,6 @@ proc main = var x = 4 x = default(int) - echo x + doAssert x == 0 main() diff --git a/tests/vm/triangle_array.nim b/tests/vm/triangle_array.nim index 054c66f22906..0704239c6323 100644 --- a/tests/vm/triangle_array.nim +++ b/tests/vm/triangle_array.nim @@ -1,7 +1,3 @@ -discard """ - output: "56" -""" - # bug #1781 proc initCombinations: array[11, array[11, int]] = @@ -14,4 +10,4 @@ proc initCombinations: array[11, array[11, int]] = result[6][6 .. 10] = [52,53,54,55,56] const combinations = initCombinations() -echo combinations[6][10] +doAssert combinations[6][10] == 56 diff --git a/tests/vm/tvarsection.nim b/tests/vm/tvarsection.nim index a45be61649af..cd34bd02ef5c 100644 --- a/tests/vm/tvarsection.nim +++ b/tests/vm/tvarsection.nim @@ -1,7 +1,3 @@ -discard """ - output: '''-1abc''' -""" - var a {.compileTime.} = 2 b = -1 @@ -12,4 +8,4 @@ static: doAssert a == 2 doAssert c == 3 -echo b, d +doAssert ($b & $d) == "-1abc" diff --git a/tests/vm/twrong_concat.nim b/tests/vm/twrong_concat.nim index b9cca8341d34..59a10bdb969f 100644 --- a/tests/vm/twrong_concat.nim +++ b/tests/vm/twrong_concat.nim @@ -1,7 +1,3 @@ -discard """ - output: '''success''' -""" - # bug #3804 #import sequtils @@ -24,5 +20,3 @@ static: # sameBug(objs) echo objs[0].field doAssert(objs[0].field == "hello") # fails, because (objs[0].field == "hello bug") - mutated! - -echo "success" diff --git a/tests/vm/tyaytypedesc.nim b/tests/vm/tyaytypedesc.nim index aab94d64353e..8cb402bff6a8 100644 --- a/tests/vm/tyaytypedesc.nim +++ b/tests/vm/tyaytypedesc.nim @@ -1,7 +1,3 @@ -discard """ - output: "ntWhitespace" -""" - # bug #3357 type NodeType* = enum @@ -18,4 +14,4 @@ const tokenTypeToNodeType = { ttWhitespace: ntWhitespace, }.enumTable(array[ttWhitespace..ttWhitespace, NodeType]) -echo tokenTypeToNodeType[ttWhitespace] +doAssert tokenTypeToNodeType[ttWhitespace] == ntWhitespace diff --git a/tests/vm/tzero_extend.nim b/tests/vm/tzero_extend.nim index 1fed5d419ca1..418dbc486b11 100644 --- a/tests/vm/tzero_extend.nim +++ b/tests/vm/tzero_extend.nim @@ -5,10 +5,10 @@ proc get_values(): (seq[int8], seq[int16], seq[int32]) = let i8 = -3'i8 let i16 = -3'i16 let i32 = -3'i32 - doAssert i8.ze == 0xFD - doAssert i8.ze64 == 0xFD - doAssert i16.ze == 0xFFFD - doAssert i16.ze64 == 0xFFFD + doAssert int(cast[uint8](i8)) == 0xFD + doAssert int64(cast[uint8](i8)) == 0xFD + doAssert int(cast[uint16](i16)) == 0xFFFD + doAssert int64(cast[uint16](i16)) == 0xFFFD result[0] = @[]; result[1] = @[]; result[2] = @[] diff --git a/tests/whenstmt/twhen_macro.nim b/tests/whenstmt/twhen_macro.nim index b5168144e464..deb1dddc9c0c 100644 --- a/tests/whenstmt/twhen_macro.nim +++ b/tests/whenstmt/twhen_macro.nim @@ -1,11 +1,5 @@ import macros -discard """ - output: ''' -when - test -''' -""" - # test that when stmt works from within a macro macro output(s: string, xs: varargs[untyped]): auto = @@ -21,4 +15,4 @@ macro output(s: string, xs: varargs[untyped]): auto = # should never get here so this should not break more.broken.xs -echo output("test") \ No newline at end of file +doAssert output("test") == "when - test" \ No newline at end of file diff --git a/tools/ci_generate.nim b/tools/ci_generate.nim index a8a80e0267c7..46bc39470f3b 100644 --- a/tools/ci_generate.nim +++ b/tools/ci_generate.nim @@ -42,7 +42,7 @@ triggers: proc genBuildExtras(echoRun, koch, nim: string): string = result = fmt""" -{echoRun} {nim} c --skipUserCfg --skipParentCfg --hints:off koch +{echoRun} {nim} c --noNimblePath --skipUserCfg --skipParentCfg --hints:off koch {echoRun} {koch} boot -d:release --skipUserCfg --skipParentCfg --hints:off {echoRun} {koch} tools --skipUserCfg --skipParentCfg --hints:off """ diff --git a/tools/compiler.gdb b/tools/compiler.gdb new file mode 100644 index 000000000000..c81f47152935 --- /dev/null +++ b/tools/compiler.gdb @@ -0,0 +1,39 @@ +# create a breakpoint on `debugutils.enteringDebugSection` +define enable_enteringDebugSection + break -function enteringDebugSection + # run these commands once breakpoint enteringDebugSection is hit + command + # enable all breakpoints and watchpoints + enable + # continue execution + cont + end +end + +# create a breakpoint on `debugutils.exitingDebugSection` named exitingDebugSection +define enable_exitingDebugSection + break -function exitingDebugSection + # run these commands once breakpoint exitingDebugSection is hit + command + # disable all breakpoints and watchpoints + disable + # but enable the enteringDebugSection breakpoint + enable_enteringDebugSection + # continue execution + cont + end +end + +# some commands can't be set until the process is running, so set an entry breakpoint +break -function NimMain +# run these commands once breakpoint NimMain is hit +command + # disable all breakpoints and watchpoints + disable + # but enable the enteringDebugSection breakpoint + enable_enteringDebugSection + # no longer need this breakpoint + delete -function NimMain + # continue execution + cont +end diff --git a/tools/compiler.lldb b/tools/compiler.lldb new file mode 100644 index 000000000000..e0b3750554cf --- /dev/null +++ b/tools/compiler.lldb @@ -0,0 +1,40 @@ +# create a breakpoint on `debugutils.enteringDebugSection` named enteringDebugSection +breakpoint set -n 'enteringDebugSection' -N enteringDebugSection +# run these commands once breakpoint enteringDebugSection is hit +breakpoint command add enteringDebugSection + # enable all breakpoints + breakpoint enable + # enable all watchpoints + # watchpoint enable # FIXME: not currently working for unknown reason + # continue execution + continue +DONE + +# create a breakpoint on `debugutils.exitingDebugSection` named exitingDebugSection +breakpoint set -n 'exitingDebugSection' -N exitingDebugSection +# run these commands once breakpoint exitingDebugSection is hit +breakpoint command add exitingDebugSection + # disable all breakpoints + breakpoint disable + # disable all watchpoints + # watchpoint disable # FIXME: not currently working for unknown reason + breakpoint enable enteringDebugSection + # continue execution + continue +DONE + +# some commands can't be set until the process is running, so set an entry breakpoint +breakpoint set -n NimMain -N NimMain +# run these commands once breakpoint NimMain is hit +breakpoint command add NimMain + # disable all breakpoints + breakpoint disable + # disable all watchpoints + # watchpoint disable # FIXME: not currently working for unknown reason + # enable the enteringDebugSection breakpoint though + breakpoint enable enteringDebugSection + # no longer need this breakpoint + breakpoint delete NimMain + # continue execution + continue +DONE diff --git a/tools/dochack/dochack.nim b/tools/dochack/dochack.nim index c7b8232efbcc..1a5c86253ba6 100644 --- a/tools/dochack/dochack.nim +++ b/tools/dochack/dochack.nim @@ -2,81 +2,50 @@ import dom import fuzzysearch -proc switchTheme(event: Event) = - if event.target.checked: - document.documentElement.setAttribute("data-theme", "dark") - window.localStorage.setItem("theme", "dark") - else: - document.documentElement.setAttribute("data-theme", "light") - window.localStorage.setItem("theme", "light") +proc setTheme(theme: cstring) {.exportc.} = + document.documentElement.setAttribute("data-theme", theme) + window.localStorage.setItem("theme", theme) + +# set `data-theme` attribute early to prevent white flash +setTheme: + let t = window.localStorage.getItem("theme") + if t.isNil: cstring"auto" else: t +proc onDOMLoaded(e: Event) {.exportc.} = + # set theme select value + document.getElementById("theme-select").value = window.localStorage.getItem("theme") -proc nimThemeSwitch(event: Event) {.exportC.} = - var pragmaDots = document.getElementsByClassName("pragmadots") - for i in 0.. 0: - document.documentElement.setAttribute("data-theme", currentTheme); - - if currentTheme == "dark" and toggleSwitch != nil: - toggleSwitch.checked = true -proc textContent(e: Element): cstring {. - importcpp: "#.textContent", nodecl.} - -proc textContent(e: Node): cstring {. - importcpp: "#.textContent", nodecl.} - -proc tree(tag: string; kids: varargs[Element]): Element = +proc tree(tag: cstring; kids: varargs[Element]): Element = result = document.createElement tag for k in kids: result.appendChild k proc add(parent, kid: Element) = - if parent.nodeName == cstring"TR" and ( - kid.nodeName == cstring"TD" or kid.nodeName == cstring"TH"): + if parent.nodeName == "TR" and (kid.nodeName == "TD" or kid.nodeName == "TH"): let k = document.createElement("TD") appendChild(k, kid) appendChild(parent, k) else: appendChild(parent, kid) -proc setClass(e: Element; value: string) = +proc setClass(e: Element; value: cstring) = e.setAttribute("class", value) -proc text(s: string): Element = cast[Element](document.createTextNode(s)) proc text(s: cstring): Element = cast[Element](document.createTextNode(s)) -proc getElementById(id: cstring): Element {.importc: "document.getElementById", nodecl.} - proc replaceById(id: cstring; newTree: Node) = - let x = getElementById(id) + let x = document.getElementById(id) x.parentNode.replaceChild(newTree, x) newTree.id = id -proc findNodeWith(x: Element; tag, content: cstring): Element = - if x.nodeName == tag and x.textContent == content: - return x - for i in 0..", "text/html"); - `stuff` = doc.documentElement; """.} db = stuff.getElementsByClass"reference" @@ -337,7 +273,7 @@ proc dosearch(value: cstring): Element = var matches: seq[(Node, int)] = @[] for i in 0.. " diff --git a/tools/nimgrep.nim b/tools/nimgrep.nim index a589cfb14eea..ef8aa55702e5 100644 --- a/tools/nimgrep.nim +++ b/tools/nimgrep.nim @@ -95,26 +95,34 @@ type filename: string, fileResult: FileResult] WalkOpt = tuple # used for walking directories/producing paths extensions: seq[string] - skipExtensions: seq[string] - excludeFile: seq[string] - includeFile: seq[string] - includeDir : seq[string] - excludeDir : seq[string] + notExtensions: seq[string] + filename: seq[string] + notFilename: seq[string] + dirPath: seq[string] + notDirPath: seq[string] + dirname : seq[string] + notDirname : seq[string] WalkOptComp[Pat] = tuple # a compiled version of the previous - excludeFile: seq[Pat] - includeFile: seq[Pat] - includeDir : seq[Pat] - excludeDir : seq[Pat] + filename: seq[Pat] + notFilename: seq[Pat] + dirname : seq[Pat] + notDirname : seq[Pat] + dirPath: seq[Pat] + notDirPath: seq[Pat] SearchOpt = tuple # used for searching inside a file - patternSet: bool # to distinguish uninitialized 'pattern' and empty one - pattern: string # main PATTERN - checkMatch: string # --match - checkNoMatch: string # --nomatch - checkBin: Bin # --bin + patternSet: bool # To distinguish uninitialized/empty 'pattern' + pattern: string # Main PATTERN + inFile: seq[string] # --inFile, --inf + notInFile: seq[string] # --notinFile, --ninf + inContext: seq[string] # --inContext, --inc + notInContext: seq[string] # --notinContext, --ninc + checkBin: Bin # --bin, --text SearchOptComp[Pat] = tuple # a compiled version of the previous pattern: Pat - checkMatch: Pat - checkNoMatch: Pat + inFile: seq[Pat] + notInFile: seq[Pat] + inContext: seq[Pat] + notInContext: seq[Pat] SinglePattern[PAT] = tuple # compile single pattern for replacef pattern: PAT Column = tuple # current column info for the cropping (--limit) feature @@ -807,6 +815,33 @@ template declareCompiledPatterns(compiledStruct: untyped, body {.hint[XDeclaredButNotUsed]: on.} +template ensureIncluded(includePat: seq[Pattern], str: string, + body: untyped) = + if includePat.len != 0: + var matched = false + for pat in includePat: + if str.contains(pat): + matched = true + break + if not matched: + body + +template ensureExcluded(excludePat: seq[Pattern], str: string, + body: untyped) = + {.warning[UnreachableCode]: off.} + for pat in excludePat: + if str.contains(pat, 0): + body + break + {.warning[UnreachableCode]: on.} + +func checkContext(context: string, searchOptC: SearchOptComp[Pattern]): bool = + ensureIncluded searchOptC.inContext, context: + return false + ensureExcluded searchOptC.notInContext, context: + return false + result = true + iterator processFile(searchOptC: SearchOptComp[Pattern], filename: string, yieldContents=false): Output = var buffer: string @@ -836,13 +871,13 @@ iterator processFile(searchOptC: SearchOptComp[Pattern], filename: string, reason = "text file" if not reject: - if searchOpt.checkMatch != "": - reject = not contains(buffer, searchOptC.checkMatch, 0) + ensureIncluded searchOptC.inFile, buffer: + reject = true reason = "doesn't contain a requested match" if not reject: - if searchOpt.checkNoMatch != "": - reject = contains(buffer, searchOptC.checkNoMatch, 0) + ensureExcluded searchOptC.notInFile, buffer: + reject = true reason = "contains a forbidden match" if reject: @@ -852,20 +887,50 @@ iterator processFile(searchOptC: SearchOptComp[Pattern], filename: string, else: var found = false var cnt = 0 - for output in searchFile(searchOptC.pattern, buffer): - found = true - if optCount notin options: - yield output - else: - if output.kind in {blockFirstMatch, blockNextMatch}: - inc(cnt) + let skipCheckContext = (searchOpt.notInContext.len == 0 and + searchOpt.inContext.len == 0) + if skipCheckContext: + for output in searchFile(searchOptC.pattern, buffer): + found = true + if optCount notin options: + yield output + else: + if output.kind in {blockFirstMatch, blockNextMatch}: + inc(cnt) + else: + var context: string + var outputAccumulator: seq[Output] + for outp in searchFile(searchOptC.pattern, buffer): + if outp.kind in {blockFirstMatch, blockNextMatch}: + outputAccumulator.add outp + context.add outp.pre + context.add outp.match.match + elif outp.kind == blockEnd: + outputAccumulator.add outp + context.add outp.blockEnding + # context has been formed, now check it: + if checkContext(context, searchOptC): + found = true + for output in outputAccumulator: + if optCount notin options: + yield output + else: + if output.kind in {blockFirstMatch, blockNextMatch}: + inc(cnt) + context = "" + outputAccumulator.setLen 0 + # end `if skipCheckContext`. if optCount in options and cnt > 0: yield Output(kind: justCount, matches: cnt) if yieldContents and found and optCount notin options: yield Output(kind: fileContents, buffer: move(buffer)) - -proc hasRightFileName(path: string, walkOptC: WalkOptComp[Pattern]): bool = +proc hasRightPath(path: string, walkOptC: WalkOptComp[Pattern]): bool = + if not ( + walkOpt.extensions.len > 0 or walkOpt.notExtensions.len > 0 or + walkOpt.filename.len > 0 or walkOpt.notFilename.len > 0 or + walkOpt.notDirPath.len > 0 or walkOpt.dirPath.len > 0): + return true let filename = path.lastPathPart let ex = filename.splitFile.ext.substr(1) # skip leading '.' if walkOpt.extensions.len != 0: @@ -875,31 +940,44 @@ proc hasRightFileName(path: string, walkOptC: WalkOptComp[Pattern]): bool = matched = true break if not matched: return false - for x in walkOpt.skipExtensions: + for x in walkOpt.notExtensions: if os.cmpPaths(x, ex) == 0: return false - if walkOptC.includeFile.len != 0: - var matched = false - for pat in walkOptC.includeFile: - if filename.contains(pat): - matched = true - break - if not matched: return false - for pat in walkOptC.excludeFile: - if filename.contains(pat): return false - let dirname = path.parentDir - if walkOptC.includeDir.len != 0: - var matched = false - for pat in walkOptC.includeDir: - if dirname.contains(pat): - matched = true + ensureIncluded walkOptC.filename, filename: + return false + ensureExcluded walkOptC.notFilename, filename: + return false + let parent = path.parentDir + ensureExcluded walkOptC.notDirPath, parent: + return false + ensureIncluded walkOptC.dirPath, parent: + return false + result = true + +proc isRightDirectory(path: string, walkOptC: WalkOptComp[Pattern]): bool = + ## --dirname can be only checked when the final path is known + ## so this proc is suitable for files only. + if walkOptC.dirname.len > 0: + var badDirname = false + var (nextParent, dirname) = splitPath(path) + # check that --dirname matches for one of directories in parent path: + while dirname != "": + badDirname = false + ensureIncluded walkOptC.dirname, dirname: + badDirname = true + if not badDirname: break - if not matched: return false + (nextParent, dirname) = splitPath(nextParent) + if badDirname: # badDirname was set to true for all the dirs + return false result = true -proc hasRightDirectory(path: string, walkOptC: WalkOptComp[Pattern]): bool = - let dirname = path.lastPathPart - for pat in walkOptC.excludeDir: - if dirname.contains(pat): return false +proc descendToDirectory(path: string, walkOptC: WalkOptComp[Pattern]): bool = + ## --notdirname can be checked for directories immediately for optimization to + ## prevent descending into undesired directories. + if walkOptC.notDirname.len > 0: + let dirname = path.lastPathPart + ensureExcluded walkOptC.notDirname, dirname: + return false result = true iterator walkDirBasic(dir: string, walkOptC: WalkOptComp[Pattern]): string @@ -908,22 +986,24 @@ iterator walkDirBasic(dir: string, walkOptC: WalkOptComp[Pattern]): string var timeFiles = newSeq[(times.Time, string)]() while dirStack.len > 0: let d = dirStack.pop() + let rightDirForFiles = d.isRightDirectory(walkOptC) var files = newSeq[string]() var dirs = newSeq[string]() for kind, path in walkDir(d): case kind of pcFile: - if path.hasRightFileName(walkOptC): + if path.hasRightPath(walkOptC) and rightDirForFiles: files.add(path) of pcLinkToFile: - if optFollow in options and path.hasRightFileName(walkOptC): + if optFollow in options and path.hasRightPath(walkOptC) and + rightDirForFiles: files.add(path) of pcDir: - if optRecursive in options and path.hasRightDirectory(walkOptC): + if optRecursive in options and path.descendToDirectory(walkOptC): dirs.add path of pcLinkToDir: if optFollow in options and optRecursive in options and - path.hasRightDirectory(walkOptC): + path.descendToDirectory(walkOptC): dirs.add path if sortTime: # sort by time - collect files before yielding for file in files: @@ -948,10 +1028,12 @@ iterator walkDirBasic(dir: string, walkOptC: WalkOptComp[Pattern]): string iterator walkRec(paths: seq[string]): tuple[error: string, filename: string] {.closure.} = declareCompiledPatterns(walkOptC, WalkOptComp): - walkOptC.excludeFile.add walkOpt.excludeFile.compileArray() - walkOptC.includeFile.add walkOpt.includeFile.compileArray() - walkOptC.includeDir.add walkOpt.includeDir.compileArray() - walkOptC.excludeDir.add walkOpt.excludeDir.compileArray() + walkOptC.notFilename.add walkOpt.notFilename.compileArray() + walkOptC.filename.add walkOpt.filename.compileArray() + walkOptC.dirname.add walkOpt.dirname.compileArray() + walkOptC.notDirname.add walkOpt.notDirname.compileArray() + walkOptC.dirPath.add walkOpt.dirPath.compileArray() + walkOptC.notDirPath.add walkOpt.notDirPath.compileArray() for path in paths: if dirExists(path): for p in walkDirBasic(path, walkOptC): @@ -1030,8 +1112,10 @@ template processFileResult(pattern: Pattern; filename: string, proc run1Thread() = declareCompiledPatterns(searchOptC, SearchOptComp): compile1Pattern(searchOpt.pattern, searchOptC.pattern) - compile1Pattern(searchOpt.checkMatch, searchOptC.checkMatch) - compile1Pattern(searchOpt.checkNoMatch, searchOptC.checkNoMatch) + searchOptC.inFile.add searchOpt.inFile.compileArray() + searchOptC.notInFile.add searchOpt.notInFile.compileArray() + searchOptC.inContext.add searchOpt.inContext.compileArray() + searchOptC.notInContext.add searchOpt.notInContext.compileArray() if optPipe in options: processFileResult(searchOptC.pattern, "-", processFile(searchOptC, "-", @@ -1073,8 +1157,10 @@ proc worker(initSearchOpt: SearchOpt) {.thread.} = searchOpt = initSearchOpt # init thread-local var declareCompiledPatterns(searchOptC, SearchOptComp): compile1Pattern(searchOpt.pattern, searchOptC.pattern) - compile1Pattern(searchOpt.checkMatch, searchOptC.checkMatch) - compile1Pattern(searchOpt.checkNoMatch, searchOptC.checkNoMatch) + searchOptC.inFile.add searchOpt.inFile.compileArray() + searchOptC.notInFile.add searchOpt.notInFile.compileArray() + searchOptC.inContext.add searchOpt.inContext.compileArray() + searchOptC.notInContext.add searchOpt.notInContext.compileArray() while true: let (fileNo, filename) = searchRequestsChan.recv() var fileResult: FileResult @@ -1197,15 +1283,35 @@ for kind, key, val in getopt(): nWorkers = countProcessors() else: nWorkers = parseNonNegative(val, key) - of "ext": walkOpt.extensions.add val.split('|') - of "noext", "no-ext": walkOpt.skipExtensions.add val.split('|') - of "excludedir", "exclude-dir", "ed": walkOpt.excludeDir.add val - of "includedir", "include-dir", "id": walkOpt.includeDir.add val - of "includefile", "include-file", "if": walkOpt.includeFile.add val - of "excludefile", "exclude-file", "ef": walkOpt.excludeFile.add val - of "match": searchOpt.checkMatch = val - of "nomatch": - searchOpt.checkNoMatch = val + of "extensions", "ex", "ext": walkOpt.extensions.add val.split('|') + of "nextensions", "notextensions", "nex", "notex", + "noext", "no-ext": # 2 deprecated options + walkOpt.notExtensions.add val.split('|') + of "dirname", "di": + walkOpt.dirname.add val + of "ndirname", "notdirname", "ndi", "notdi", + "excludedir", "ed": # 2 deprecated options + walkOpt.notDirname.add val + of "dirpath", "dirp", + "includedir", "id": # 2 deprecated options + walkOpt.dirPath.add val + of "ndirpath", "notdirpath", "ndirp", "notdirp": + walkOpt.notDirPath.add val + of "filename", "fi", + "includefile", "include-file", "if": # 3 deprecated options + walkOpt.filename.add val + of "nfilename", "nfi", "notfilename", "notfi", + "excludefile", "exclude-file", "ef": # 3 deprecated options + walkOpt.notFilename.add val + of "infile", "inf", + "matchfile", "match", "mf": # 3 deprecated options + searchOpt.inFile.add val + of "ninfile", "notinfile", "ninf", "notinf", + "nomatchfile", "nomatch", "nf": # 3 options are deprecated + searchOpt.notInFile.add val + of "incontext", "inc": searchOpt.inContext.add val + of "nincontext", "notincontext", "ninc", "notinc": + searchOpt.notInContext.add val of "bin": case val of "on": searchOpt.checkBin = biOn diff --git a/tools/niminst/buildsh.nimf b/tools/niminst/buildsh.nimf index 7a05ef342fad..6b99c49eebbe 100644 --- a/tools/niminst/buildsh.nimf +++ b/tools/niminst/buildsh.nimf @@ -164,7 +164,7 @@ esac case $ucpu in *i386* | *i486* | *i586* | *i686* | *bepc* | *i86pc* ) - if [ "$isOpenIndiana" = "yes" ] ; then + if [ "$isOpenIndiana" = "yes" ] || [ `uname -o` == "illumos" ] ; then mycpu="amd64" else mycpu="i386" diff --git a/tools/niminst/install.nimf b/tools/niminst/install.nimf index c2fc96e94d43..75ff9ce11899 100644 --- a/tools/niminst/install.nimf +++ b/tools/niminst/install.nimf @@ -6,19 +6,19 @@ set -e if [ $# -eq 1 ] ; then -# if c.cat[fcUnixBin].len > 0: - if test -f ?{c.cat[fcUnixBin][0].toUnix} +#if c.cat[fcUnixBin].len > 0: + if [ -f "?{c.cat[fcUnixBin][0].toUnix}" ] then echo "?c.displayName build detected" else echo "Please build ?c.displayName before installing it" exit 1 fi -# end if +#end if case $1 in "--help"|"-h"|"help"|"h") echo "?c.displayName installation script" - echo "Usage: [sudo] sh install.sh DIR" + echo "Usage: [sudo] [env DESTDIR=...] sh install.sh DIR" echo "Where DIR may be:" echo " /usr/bin" echo " /usr/local/bin" @@ -29,19 +29,19 @@ if [ $# -eq 1 ] ; then exit 1 ;; "/usr/bin") - bindir=/usr/bin - configdir=/etc/?proj - libdir=/usr/lib/?proj - docdir=/usr/share/?proj/doc - datadir=/usr/share/?proj/data + bindir=$1 + configdir="/etc/?proj" + libdir="/usr/lib/?proj" + docdir="/usr/share/?proj/doc" + datadir="/usr/share/?proj/data" nimbleDir="/opt/nimble/pkgs/?c.nimblePkgName-?c.version" ;; "/usr/local/bin") - bindir=/usr/local/bin - configdir=/etc/?proj - libdir=/usr/local/lib/?proj - docdir=/usr/local/share/?proj/doc - datadir=/usr/local/share/?proj/data + bindir=$1 + configdir="/etc/?proj" + libdir="/usr/local/lib/?proj" + docdir="/usr/local/share/?proj/doc" + datadir="/usr/local/share/?proj/data" nimbleDir="/opt/nimble/pkgs/?c.nimblePkgName-?c.version" ;; "/opt") @@ -51,9 +51,6 @@ if [ $# -eq 1 ] ; then docdir="/opt/?proj/doc" datadir="/opt/?proj/data" nimbleDir="/opt/nimble/pkgs/?c.nimblePkgName-?c.version" - mkdir -p /opt/?proj - mkdir -p $bindir - mkdir -p $configdir ;; *) bindir="$1/?proj/bin" @@ -62,16 +59,22 @@ if [ $# -eq 1 ] ; then docdir="$1/?proj/doc" datadir="$1/?proj/data" nimbleDir="$1/?proj" - mkdir -p $1/?proj - mkdir -p $bindir - mkdir -p $configdir ;; esac - mkdir -p $libdir - mkdir -p $docdir - mkdir -p $configdir - mkdir -p $nimbleDir/ + bindir="${DESTDIR}${bindir}" + configdir="${DESTDIR}${configdir}" + libdir="${DESTDIR}${libdir}" + docdir="${DESTDIR}${docdir}" + datadir="${DESTDIR}${datadir}" + nimbleDir="${DESTDIR}${nimbleDir}" + + mkdir -p "$bindir" + mkdir -p "$configdir" + mkdir -p "$libdir" + mkdir -p "$docdir" + mkdir -p "$datadir" + mkdir -p "$nimbleDir" echo "copying files..." #var createdDirs = newStringTable() #for cat in {fcConfig..fcLib, fcNimble}: @@ -84,46 +87,46 @@ if [ $# -eq 1 ] ; then # end if # if mk.len > 0 and not createdDirs.hasKey(mk): # createdDirs[mk] = "true" - mkdir -p ?{mk.toUnix} + mkdir -p "?{mk.toUnix}" # end if # end for #end for #for f in items(c.cat[fcUnixBin]): - cp ?f.toUnix $bindir/?f.skipRoot.toUnix - chmod 755 $bindir/?f.skipRoot.toUnix + cp "?f.toUnix" "$bindir/?f.skipRoot.toUnix" + chmod 755 "$bindir/?f.skipRoot.toUnix" #end for #for f in items(c.cat[fcConfig]): - cp ?f.toUnix $configdir/?f.skipRoot.toUnix - chmod 644 $configdir/?f.skipRoot.toUnix + cp "?f.toUnix" "$configdir/?f.skipRoot.toUnix" + chmod 644 "$configdir/?f.skipRoot.toUnix" #end for #for f in items(c.cat[fcData]): - if [ -f ?f.toUnix ]; then - cp ?f.toUnix $datadir/?f.skipRoot.toUnix - chmod 644 $datadir/?f.skipRoot.toUnix + if [ -f "?f.toUnix" ]; then + cp "?f.toUnix" "$datadir/?f.skipRoot.toUnix" + chmod 644 "$datadir/?f.skipRoot.toUnix" fi #end for #for f in items(c.cat[fcDoc]): - if [ -f ?f.toUnix ]; then - cp ?f.toUnix $docdir/?f.skipRoot.toUnix - chmod 644 $docdir/?f.skipRoot.toUnix + if [ -f "?f.toUnix" ]; then + cp "?f.toUnix" "$docdir/?f.skipRoot.toUnix" + chmod 644 "$docdir/?f.skipRoot.toUnix" fi #end for #for f in items(c.cat[fcLib]): - cp ?f.toUnix $libdir/?f.skipRoot.toUnix - chmod 644 $libdir/?f.skipRoot.toUnix + cp "?f.toUnix" "$libdir/?f.skipRoot.toUnix" + chmod 644 "$libdir/?f.skipRoot.toUnix" #end for #for f in items(c.cat[fcNimble]): - cp ?f.toUnix $nimbleDir/?f.toUnix - chmod 644 $nimbleDir/?f.toUnix + cp "?f.toUnix" "$nimbleDir/?f.toUnix" + chmod 644 "$nimbleDir/?f.toUnix" #end for -cp ?{c.nimblePkgName}.nimble $nimbleDir/?{c.nimblePkgName}.nimble -chmod 644 $nimbleDir/?{c.nimblePkgName}.nimble +cp "?{c.nimblePkgName}.nimble" "$nimbleDir/?{c.nimblePkgName}.nimble" +chmod 644 "$nimbleDir/?{c.nimblePkgName}.nimble" echo "installation successful" else echo "?c.displayName installation script" - echo "Usage: [sudo] sh install.sh DIR" + echo "Usage: [sudo] [env DESTDIR=...] sh install.sh DIR" echo "Where DIR may be:" echo " /usr/bin" echo " /usr/local/bin" diff --git a/tools/nimweb.nim b/tools/nimweb.nim deleted file mode 100644 index ccc80dfcf1d5..000000000000 --- a/tools/nimweb.nim +++ /dev/null @@ -1,556 +0,0 @@ -# -# -# Nim Website Generator -# (c) Copyright 2015 Andreas Rumpf -# -# See the file "copying.txt", included in this -# distribution, for details about the copyright. -# - -import - os, strutils, times, parseopt, parsecfg, streams, strtabs, tables, - re, htmlgen, macros, md5, osproc, parsecsv, algorithm - -from xmltree import escape - -type - TKeyValPair = tuple[key, id, val: string] - TConfigData = object of RootObj - tabs, links: seq[TKeyValPair] - doc, srcdoc, srcdoc2, webdoc, pdf: seq[string] - authors, projectName, projectTitle, logo, infile, ticker: string - vars: StringTableRef - nimCompiler: string - nimArgs: string - gitURL: string - docHTMLOutput: string - webUploadOutput: string - quotations: Table[string, tuple[quote, author: string]] - numProcessors: int # Set by parallelBuild:n, only works for values > 0. - gaId: string # google analytics ID, nil means analytics are disabled - TRssItem = object - year, month, day, title, url, content: string - TAction = enum - actAll, actOnlyWebsite, actPdf, actJson2, actOnlyDocs - - Sponsor = object - logo: string - name: string - url: string - thisMonth: int - allTime: int - since: string - level: int - -var action: TAction - -proc initConfigData(c: var TConfigData) = - c.tabs = @[] - c.links = @[] - c.doc = @[] - c.srcdoc = @[] - c.srcdoc2 = @[] - c.webdoc = @[] - c.pdf = @[] - c.infile = "" - c.nimArgs = "--hint:Conf:off --hint:Path:off --hint:Processing:off -d:boot " - c.gitURL = "https://github.com/nim-lang/Nim" - c.docHTMLOutput = "doc/html" - c.webUploadOutput = "web/upload" - c.authors = "" - c.projectTitle = "" - c.projectName = "" - c.logo = "" - c.ticker = "" - c.vars = newStringTable(modeStyleInsensitive) - c.numProcessors = countProcessors() - # Attempts to obtain the git current commit. - when false: - let (output, code) = execCmdEx("git log -n 1 --format=%H") - if code == 0 and output.strip.len == 40: - c.gitCommit = output.strip - c.quotations = initTable[string, tuple[quote, author: string]]() - -include "website.nimf" - -# ------------------------- configuration file ------------------------------- - -const - version = "0.8" - usage = "nimweb - Nim Website Generator Version " & version & """ - - (c) 2015 Andreas Rumpf -Usage: - nimweb [options] ini-file[.ini] [compile_options] -Options: - -h, --help shows this help - -v, --version shows the version - -o, --output overrides output directory instead of default - web/upload and doc/html - --nimCompiler overrides nim compiler; default = bin/nim - --var:name=value set the value of a variable - --website only build the website, not the full documentation - --pdf build the PDF version of the documentation - --json2 build JSON of the documentation - --onlyDocs build only the documentation - --git.url override base url in generated doc links - --git.commit override commit/branch in generated doc links 'source' - --git.devel override devel branch in generated doc links 'edit' -Compile_options: - will be passed to the Nim compiler -""" - - rYearMonthDay = r"on\s+(\d{2})\/(\d{2})\/(\d{4})" - rssUrl = "http://nim-lang.org/news.xml" - rssNewsUrl = "http://nim-lang.org/news.html" - activeSponsors = "web/sponsors.csv" - inactiveSponsors = "web/inactive_sponsors.csv" - validAnchorCharacters = Letters + Digits - - -macro id(e: untyped): untyped = - ## generates the rss xml ``id`` element. - let e = callsite() - result = xmlCheckedTag(e, "id") - -macro updated(e: varargs[untyped]): untyped = - ## generates the rss xml ``updated`` element. - let e = callsite() - result = xmlCheckedTag(e, "updated") - -proc updatedDate(year, month, day: string): string = - ## wrapper around the update macro with easy input. - result = updated("$1-$2-$3T00:00:00Z" % [year, - repeat("0", 2 - len(month)) & month, - repeat("0", 2 - len(day)) & day]) - -macro entry(e: varargs[untyped]): untyped = - ## generates the rss xml ``entry`` element. - let e = callsite() - result = xmlCheckedTag(e, "entry") - -macro content(e: varargs[untyped]): untyped = - ## generates the rss xml ``content`` element. - let e = callsite() - result = xmlCheckedTag(e, "content", reqAttr = "type") - -proc parseCmdLine(c: var TConfigData) = - var p = initOptParser() - while true: - next(p) - var kind = p.kind - var key = p.key - var val = p.val - case kind - of cmdArgument: - c.infile = addFileExt(key, "ini") - c.nimArgs.add(cmdLineRest(p)) - break - of cmdLongOption, cmdShortOption: - case normalize(key) - of "help", "h": - stdout.write(usage) - quit(0) - of "version", "v": - stdout.write(version & "\n") - quit(0) - of "output", "o": - c.webUploadOutput = val - c.docHTMLOutput = val / "docs" - of "nimcompiler": - c.nimCompiler = val - of "parallelbuild": - try: - let num = parseInt(val) - if num != 0: c.numProcessors = num - except ValueError: - quit("invalid numeric value for --parallelBuild") - of "var": - var idx = val.find('=') - if idx < 0: quit("invalid command line") - c.vars[substr(val, 0, idx-1)] = substr(val, idx+1) - of "website": action = actOnlyWebsite - of "pdf": action = actPdf - of "json2": action = actJson2 - of "onlydocs": action = actOnlyDocs - of "googleanalytics": - c.gaId = val - c.nimArgs.add("--doc.googleAnalytics:" & val & " ") - of "git.url": - c.gitURL = val - of "git.commit": - c.nimArgs.add("--git.commit:" & val & " ") - of "git.devel": - c.nimArgs.add("--git.devel:" & val & " ") - else: - echo("Invalid argument '$1'" % [key]) - quit(usage) - of cmdEnd: break - if c.infile.len == 0: quit(usage) - -proc walkDirRecursively(s: var seq[string], root, ext: string) = - for k, f in walkDir(root): - case k - of pcFile, pcLinkToFile: - if cmpIgnoreCase(ext, splitFile(f).ext) == 0: - add(s, f) - of pcDir: walkDirRecursively(s, f, ext) - of pcLinkToDir: discard - -proc addFiles(s: var seq[string], dir, ext: string, patterns: seq[string]) = - for p in items(patterns): - if fileExists(dir / addFileExt(p, ext)): - s.add(dir / addFileExt(p, ext)) - if dirExists(dir / p): - walkDirRecursively(s, dir / p, ext) - -proc parseIniFile(c: var TConfigData) = - var - p: CfgParser - section: string # current section - var input = newFileStream(c.infile, fmRead) - if input == nil: quit("cannot open: " & c.infile) - open(p, input, c.infile) - while true: - var k = next(p) - case k.kind - of cfgEof: break - of cfgSectionStart: - section = normalize(k.section) - case section - of "project", "links", "tabs", "ticker", "documentation", "var": discard - else: echo("[Warning] Skipping unknown section: " & section) - - of cfgKeyValuePair: - var v = k.value % c.vars - c.vars[k.key] = v - - case section - of "project": - case normalize(k.key) - of "name": c.projectName = v - of "title": c.projectTitle = v - of "logo": c.logo = v - of "authors": c.authors = v - else: quit(errorStr(p, "unknown variable: " & k.key)) - of "var": discard - of "links": - let valID = v.split(';') - add(c.links, (k.key.replace('_', ' '), valID[1], valID[0])) - of "tabs": add(c.tabs, (k.key, "", v)) - of "ticker": c.ticker = v - of "documentation": - case normalize(k.key) - of "doc": addFiles(c.doc, "doc", ".rst", split(v, {';'})) - of "pdf": addFiles(c.pdf, "doc", ".rst", split(v, {';'})) - of "srcdoc": addFiles(c.srcdoc, "lib", ".nim", split(v, {';'})) - of "srcdoc2": addFiles(c.srcdoc2, "lib", ".nim", split(v, {';'})) - of "webdoc": addFiles(c.webdoc, "lib", ".nim", split(v, {';'})) - of "parallelbuild": - try: - let num = parseInt(v) - if num != 0: c.numProcessors = num - except ValueError: - quit("invalid numeric value for --parallelBuild in config") - else: quit(errorStr(p, "unknown variable: " & k.key)) - of "quotations": - let vSplit = v.split('-') - doAssert vSplit.len == 2 - c.quotations[k.key.normalize] = (vSplit[0], vSplit[1]) - else: discard - of cfgOption: quit(errorStr(p, "syntax error")) - of cfgError: quit(errorStr(p, k.msg)) - close(p) - if c.projectName.len == 0: - c.projectName = changeFileExt(extractFilename(c.infile), "") - -# ------------------- main ---------------------------------------------------- - - -proc exe(f: string): string = return addFileExt(f, ExeExt) - -proc findNim(c: TConfigData): string = - if c.nimCompiler.len > 0: return c.nimCompiler - var nim = "nim".exe - result = "bin" / nim - if fileExists(result): return - for dir in split(getEnv("PATH"), PathSep): - if fileExists(dir / nim): return dir / nim - # assume there is a symlink to the exe or something: - return nim - -proc exec(cmd: string) = - echo(cmd) - let (outp, exitCode) = osproc.execCmdEx(cmd) - if exitCode != 0: quit outp - -proc sexec(cmds: openarray[string]) = - ## Serial queue wrapper around exec. - for cmd in cmds: exec(cmd) - -proc mexec(cmds: openarray[string], processors: int) = - ## Multiprocessor version of exec - doAssert processors > 0, "nimweb needs at least one processor" - if processors == 1: - sexec(cmds) - return - let r = execProcesses(cmds, {poStdErrToStdOut, poParentStreams, poEchoCmd}, - n = processors) - if r != 0: - echo "external program failed, retrying serial work queue for logs!" - sexec(cmds) - -proc buildDocSamples(c: var TConfigData, destPath: string) = - ## Special case documentation sample proc. - ## - ## TODO: consider integrating into the existing generic documentation builders - ## now that we have a single `doc` command. - exec(findNim(c) & " doc $# -o:$# $#" % - [c.nimArgs, destPath / "docgen_sample.html", "doc" / "docgen_sample.nim"]) - -proc pathPart(d: string): string = splitFile(d).dir.replace('\\', '/') - -proc buildDoc(c: var TConfigData, destPath: string) = - # call nim for the documentation: - var - commands = newSeq[string](len(c.doc) + len(c.srcdoc) + len(c.srcdoc2)) - i = 0 - for d in items(c.doc): - commands[i] = findNim(c) & " rst2html $# --git.url:$# -o:$# --index:on $#" % - [c.nimArgs, c.gitURL, - destPath / changeFileExt(splitFile(d).name, "html"), d] - i.inc - for d in items(c.srcdoc): - commands[i] = findNim(c) & " doc0 $# --git.url:$# -o:$# --index:on $#" % - [c.nimArgs, c.gitURL, - destPath / changeFileExt(splitFile(d).name, "html"), d] - i.inc - for d in items(c.srcdoc2): - commands[i] = findNim(c) & " doc $# --git.url:$# -o:$# --index:on $#" % - [c.nimArgs, c.gitURL, - destPath / changeFileExt(splitFile(d).name, "html"), d] - i.inc - - mexec(commands, c.numProcessors) - exec(findNim(c) & " buildIndex -o:$1/theindex.html $1" % [destPath]) - -proc buildPdfDoc(c: var TConfigData, destPath: string) = - createDir(destPath) - if os.execShellCmd("pdflatex -version") != 0: - echo "pdflatex not found; no PDF documentation generated" - else: - const pdflatexcmd = "pdflatex -interaction=nonstopmode " - for d in items(c.pdf): - exec(findNim(c) & " rst2tex $# $#" % [c.nimArgs, d]) - # call LaTeX twice to get cross references right: - exec(pdflatexcmd & changeFileExt(d, "tex")) - exec(pdflatexcmd & changeFileExt(d, "tex")) - # delete all the crappy temporary files: - let pdf = splitFile(d).name & ".pdf" - let dest = destPath / pdf - removeFile(dest) - moveFile(dest=dest, source=pdf) - removeFile(changeFileExt(pdf, "aux")) - if fileExists(changeFileExt(pdf, "toc")): - removeFile(changeFileExt(pdf, "toc")) - removeFile(changeFileExt(pdf, "log")) - removeFile(changeFileExt(pdf, "out")) - removeFile(changeFileExt(d, "tex")) - -proc buildAddDoc(c: var TConfigData, destPath: string) = - # build additional documentation (without the index): - var commands = newSeq[string](c.webdoc.len) - for i, doc in pairs(c.webdoc): - commands[i] = findNim(c) & " doc $# --git.url:$# -o:$# $#" % - [c.nimArgs, c.gitURL, - destPath / changeFileExt(splitFile(doc).name, "html"), doc] - mexec(commands, c.numProcessors) - -proc parseNewsTitles(inputFilename: string): seq[TRssItem] = - # Goes through each news file, returns its date/title. - result = @[] - var matches: array[3, string] - let reYearMonthDay = re(rYearMonthDay) - for kind, path in walkDir(inputFilename): - let (dir, name, ext) = path.splitFile - if ext == ".rst": - let content = readFile(path) - let title = content.splitLines()[0] - let urlPath = "news/" & name & ".html" - if content.find(reYearMonthDay, matches) >= 0: - result.add(TRssItem(year: matches[2], month: matches[1], day: matches[0], - title: title, url: "http://nim-lang.org/" & urlPath, - content: content)) - result.reverse() - -proc genUUID(text: string): string = - # Returns a valid RSS uuid, which is basically md5 with dashes and a prefix. - result = getMD5(text) - result.insert("-", 20) - result.insert("-", 16) - result.insert("-", 12) - result.insert("-", 8) - result.insert("urn:uuid:") - -proc genNewsLink(title: string): string = - # Mangles a title string into an expected news.html anchor. - result = title - result.insert("Z") - for i in 1..len(result)-1: - let letter = result[i].toLowerAscii() - if letter in validAnchorCharacters: - result[i] = letter - else: - result[i] = '-' - result.insert(rssNewsUrl & "#") - -proc generateRss(outputFilename: string, news: seq[TRssItem]) = - # Given a list of rss items generates an rss overwriting destination. - var - output: File - - if not open(output, outputFilename, mode = fmWrite): - quit("Could not write to $1 for rss generation" % [outputFilename]) - defer: output.close() - - output.write(""" - -""") - output.write(title("Nim website news")) - output.write(link(href = rssUrl, rel = "self")) - output.write(link(href = rssNewsUrl)) - output.write(id(rssNewsUrl)) - - let now = utc(getTime()) - output.write(updatedDate($now.year, $(int(now.month) + 1), $now.monthday)) - - for rss in news: - output.write(entry( - title(xmltree.escape(rss.title)), - id(genUUID(rss.title)), - link(`type` = "text/html", rel = "alternate", - href = rss.url), - updatedDate(rss.year, rss.month, rss.day), - "Nim", - content(xmltree.escape(rss.content), `type` = "text") - )) - - output.write("""""") - -proc buildNewsRss(c: var TConfigData, destPath: string) = - # generates an xml feed from the web/news.rst file - let - srcFilename = "web" / "news" - destFilename = destPath / changeFileExt(splitFile(srcFilename).name, "xml") - - generateRss(destFilename, parseNewsTitles(srcFilename)) - -proc readSponsors(sponsorsFile: string): seq[Sponsor] = - result = @[] - var fileStream = newFileStream(sponsorsFile, fmRead) - if fileStream == nil: quit("Cannot open sponsors.csv file: " & sponsorsFile) - var parser: CsvParser - open(parser, fileStream, sponsorsFile) - discard readRow(parser) # Skip the header row. - while readRow(parser): - result.add(Sponsor(logo: parser.row[0], name: parser.row[1], - url: parser.row[2], thisMonth: parser.row[3].parseInt, - allTime: parser.row[4].parseInt, - since: parser.row[5], level: parser.row[6].parseInt)) - parser.close() - -proc buildSponsors(c: var TConfigData, outputDir: string) = - let sponsors = generateSponsorsPage(readSponsors(activeSponsors), - readSponsors(inactiveSponsors)) - let outFile = outputDir / "sponsors.html" - var f: File - if open(f, outFile, fmWrite): - writeLine(f, generateHtmlPage(c, "", "Our Sponsors", sponsors, "")) - close(f) - else: - quit("[Error] Cannot write file: " & outFile) - -const - cmdRst2Html = " rst2html --compileonly $1 -o:web/$2.temp web/$2.rst" - -proc buildPage(c: var TConfigData, file, title, rss: string, assetDir = "") = - exec(findNim(c) & cmdRst2Html % [c.nimArgs, file]) - var temp = "web" / changeFileExt(file, "temp") - var content: string - try: - content = readFile(temp) - except IOError: - quit("[Error] cannot open: " & temp) - var f: File - var outfile = c.webUploadOutput / "$#.html" % file - if not dirExists(outfile.splitFile.dir): - createDir(outfile.splitFile.dir) - if open(f, outfile, fmWrite): - writeLine(f, generateHTMLPage(c, file, title, content, rss, assetDir)) - close(f) - else: - quit("[Error] cannot write file: " & outfile) - removeFile(temp) - -proc buildNews(c: var TConfigData, newsDir: string, outputDir: string) = - for kind, path in walkDir(newsDir): - let (dir, name, ext) = path.splitFile - if ext == ".rst": - let title = readFile(path).splitLines()[0] - buildPage(c, tailDir(dir) / name, title, "", "../") - else: - echo("Skipping file in news directory: ", path) - -proc buildWebsite(c: var TConfigData) = - if c.ticker.len > 0: - try: - c.ticker = readFile("web" / c.ticker) - except IOError: - quit("[Error] cannot open: " & c.ticker) - for i in 0..c.tabs.len-1: - var file = c.tabs[i].val - let rss = if file in ["news", "index"]: extractFilename(rssUrl) else: "" - if '.' in file: continue - buildPage(c, file, if file == "question": "FAQ" else: file, rss) - copyDir("web/assets", c.webUploadOutput / "assets") - buildNewsRss(c, c.webUploadOutput) - buildSponsors(c, c.webUploadOutput) - buildNews(c, "web/news", c.webUploadOutput / "news") - -proc onlyDocs(c: var TConfigData) = - createDir(c.docHTMLOutput) - buildDocSamples(c, c.docHTMLOutput) - buildDoc(c, c.docHTMLOutput) - -proc main(c: var TConfigData) = - buildWebsite(c) - let docup = c.webUploadOutput / NimVersion - createDir(docup) - buildAddDoc(c, docup) - buildDocSamples(c, docup) - buildDoc(c, docup) - onlyDocs(c) - -proc json2(c: var TConfigData) = - const destPath = "web/json2" - var commands = newSeq[string](c.srcdoc2.len) - var i = 0 - for d in items(c.srcdoc2): - createDir(destPath / splitFile(d).dir) - commands[i] = findNim(c) & " jsondoc $# --git.url:$# -o:$# --index:on $#" % - [c.nimArgs, c.gitURL, - destPath / changeFileExt(d, "json"), d] - i.inc - - mexec(commands, c.numProcessors) - -var c: TConfigData -initConfigData(c) -parseCmdLine(c) -parseIniFile(c) -case action -of actOnlyWebsite: buildWebsite(c) -of actPdf: buildPdfDoc(c, "doc/pdf") -of actOnlyDocs: onlyDocs(c) -of actAll: main(c) -of actJson2: json2(c) diff --git a/tools/urldownloader.nim b/tools/urldownloader.nim deleted file mode 100644 index 73e4034c9d7f..000000000000 --- a/tools/urldownloader.nim +++ /dev/null @@ -1,431 +0,0 @@ - -# -# -# Windows native FTP/HTTP/HTTPS file downloader -# (c) Copyright 2017 Eugene Kabanov -# -# See the file "LICENSE", included in this -# distribution, for details about the copyright. -# - -## This module implements native Windows FTP/HTTP/HTTPS downloading feature, -## using ``urlmon.UrlDownloadToFile()``. -## -## - -when not (defined(windows) or defined(nimdoc)): - {.error: "Platform is not supported.".} - -import os - -type - DownloadOptions* = enum - ## Available download options - optUseCache, ## Use Windows cache. - optUseProgressCallback, ## Report progress via callback. - optIgnoreSecurity ## Ignore HTTPS security problems. - - DownloadStatus* = enum - ## Available download status sent to ``progress`` callback. - statusProxyDetecting, ## Automatic Proxy detection. - statusCookieSent ## Cookie will be sent with request. - statusResolving, ## Resolving URL with DNS. - statusConnecting, ## Establish connection to server. - statusRedirecting ## HTTP redirection pending. - statusRequesting, ## Sending request to server. - statusMimetypeAvailable, ## Mimetype received from server. - statusBeginDownloading, ## Download process starting. - statusDownloading, ## Download process pending. - statusEndDownloading, ## Download process finished. - statusCacheAvailable ## File found in Windows cache. - statusUnsupported ## Unsupported status. - statusError ## Error happens. - - DownloadProgressCallback* = proc(status: DownloadStatus, progress: uint, - progressMax: uint, - message: string) - ## Progress callback. - ## - ## status - ## Indicate current stage of downloading process. - ## - ## progress - ## Number of bytes currently downloaded. Available only, if ``status`` is - ## ``statusBeginDownloading``, ``statusDownloading`` or - ## ``statusEndDownloading``. - ## - ## progressMax - ## Number of bytes expected to download. Available only, if ``status`` is - ## ``statusBeginDownloading``, ``statusDownloading`` or - ## ``statusEndDownloading``. - ## - ## message - ## Status message, which depends on ``status`` code. - ## - ## Available messages' values: - ## - ## statusResolving - ## URL hostname to be resolved. - ## statusConnecting - ## IP address - ## statusMimetypeAvailable - ## Downloading resource MIME type. - ## statusCacheAvailable - ## Path to filename stored in Windows cache. - -type - UUID = array[4, uint32] - - LONG = clong - ULONG = culong - HRESULT = clong - DWORD = uint32 - OLECHAR = uint16 - OLESTR = ptr OLECHAR - LPWSTR = OLESTR - UINT = cuint - REFIID = ptr UUID - -const - E_NOINTERFACE = 0x80004002'i32 - E_NOTIMPL = 0x80004001'i32 - S_OK = 0x00000000'i32 - - CP_UTF8 = 65001'u32 - - IID_IUnknown = UUID([0'u32, 0'u32, 192'u32, 1174405120'u32]) - IID_IBindStatusCallback = UUID([2045430209'u32, 298760953'u32, - 2852160140'u32, 195644160'u32]) - - BINDF_GETNEWESTVERSION = 0x00000010'u32 - BINDF_IGNORESECURITYPROBLEM = 0x00000100'u32 - BINDF_RESYNCHRONIZE = 0x00000200'u32 - BINDF_NO_UI = 0x00000800'u32 - BINDF_SILENTOPERATION = 0x00001000'u32 - BINDF_PRAGMA_NO_CACHE = 0x00002000'u32 - - ERROR_FILE_NOT_FOUND = 2 - ERROR_ACCESS_DENIED = 5 - - BINDSTATUS_FINDINGRESOURCE = 1 - BINDSTATUS_CONNECTING = 2 - BINDSTATUS_REDIRECTING = 3 - BINDSTATUS_BEGINDOWNLOADDATA = 4 - BINDSTATUS_DOWNLOADINGDATA = 5 - BINDSTATUS_ENDDOWNLOADDATA = 6 - BINDSTATUS_SENDINGREQUEST = 11 - BINDSTATUS_MIMETYPEAVAILABLE = 13 - BINDSTATUS_CACHEFILENAMEAVAILABLE = 14 - BINDSTATUS_PROXYDETECTING = 32 - BINDSTATUS_COOKIE_SENT = 34 - -type - STGMEDIUM = object - tymed: DWORD - pstg: pointer - pUnkForRelease: pointer - - SECURITY_ATTRIBUTES = object - nLength*: uint32 - lpSecurityDescriptor*: pointer - bInheritHandle*: int32 - - BINDINFO = object - cbSize: ULONG - stgmedData: STGMEDIUM - szExtraInfo: LPWSTR - grfBindInfoF: DWORD - dwBindVerb: DWORD - szCustomVerb: LPWSTR - cbstgmedData: DWORD - dwOptions: DWORD - dwOptionsFlags: DWORD - dwCodePage: DWORD - securityAttributes: SECURITY_ATTRIBUTES - iid: UUID - pUnk: pointer - dwReserved: DWORD - - IBindStatusCallback = object - vtable: ptr IBindStatusCallbackVTable - options: set[DownloadOptions] - objectRefCount: ULONG - binfoFlags: DWORD - progressCallback: DownloadProgressCallback - - PIBindStatusCallback = ptr IBindStatusCallback - LPBINDSTATUSCALLBACK = PIBindStatusCallback - - IBindStatusCallbackVTable = object - QueryInterface: proc (self: PIBindStatusCallback, - riid: ptr UUID, - pvObject: ptr pointer): HRESULT {.gcsafe,stdcall.} - AddRef: proc(self: PIBindStatusCallback): ULONG {.gcsafe, stdcall.} - Release: proc(self: PIBindStatusCallback): ULONG {.gcsafe, stdcall.} - OnStartBinding: proc(self: PIBindStatusCallback, - dwReserved: DWORD, pib: pointer): HRESULT - {.gcsafe, stdcall.} - GetPriority: proc(self: PIBindStatusCallback, pnPriority: ptr LONG): HRESULT - {.gcsafe, stdcall.} - OnLowResource: proc(self: PIBindStatusCallback, dwReserved: DWORD): HRESULT - {.gcsafe, stdcall.} - OnProgress: proc(self: PIBindStatusCallback, ulProgress: ULONG, - ulProgressMax: ULONG, ulStatusCode: ULONG, - szStatusText: LPWSTR): HRESULT - {.gcsafe, stdcall.} - OnStopBinding: proc(self: PIBindStatusCallback, hresult: HRESULT, - szError: LPWSTR): HRESULT - {.gcsafe, stdcall.} - GetBindInfo: proc(self: PIBindStatusCallback, grfBINDF: ptr DWORD, - pbindinfo: ptr BINDINFO): HRESULT - {.gcsafe, stdcall.} - OnDataAvailable: proc(self: PIBindStatusCallback, grfBSCF: DWORD, - dwSize: DWORD, pformatetc: pointer, - pstgmed: pointer): HRESULT - {.gcsafe, stdcall.} - OnObjectAvailable: proc(self: PIBindStatusCallback, riid: REFIID, - punk: pointer): HRESULT - {.gcsafe, stdcall.} - -template FAILED(hr: HRESULT): bool = - (hr < 0) - -proc URLDownloadToFile(pCaller: pointer, szUrl: LPWSTR, szFileName: LPWSTR, - dwReserved: DWORD, - lpfnCb: LPBINDSTATUSCALLBACK): HRESULT - {.stdcall, dynlib: "urlmon.dll", importc: "URLDownloadToFileW".} - -proc WideCharToMultiByte(CodePage: UINT, dwFlags: DWORD, - lpWideCharStr: ptr OLECHAR, cchWideChar: cint, - lpMultiByteStr: ptr char, cbMultiByte: cint, - lpDefaultChar: ptr char, - lpUsedDefaultChar: ptr uint32): cint - {.stdcall, dynlib: "kernel32.dll", importc: "WideCharToMultiByte".} - -proc MultiByteToWideChar(CodePage: UINT, dwFlags: DWORD, - lpMultiByteStr: ptr char, cbMultiByte: cint, - lpWideCharStr: ptr OLECHAR, cchWideChar: cint): cint - {.stdcall, dynlib: "kernel32.dll", importc: "MultiByteToWideChar".} -proc DeleteUrlCacheEntry(lpszUrlName: LPWSTR): int32 - {.stdcall, dynlib: "wininet.dll", importc: "DeleteUrlCacheEntryW".} - -proc `==`(a, b: UUID): bool = - result = false - if a[0] == b[0] and a[1] == b[1] and - a[2] == b[2] and a[3] == b[3]: - result = true - -proc `$`(bstr: LPWSTR): string = - var buffer: char - var count = WideCharToMultiByte(CP_UTF8, 0, bstr, -1, addr(buffer), 0, - nil, nil) - if count == 0: - raiseOsError(osLastError()) - else: - result = newString(count + 8) - let res = WideCharToMultiByte(CP_UTF8, 0, bstr, -1, addr(result[0]), count, - nil, nil) - if res == 0: - raiseOsError(osLastError()) - result.setLen(res - 1) - -proc toBstring(str: string): LPWSTR = - var buffer: OLECHAR - var count = MultiByteToWideChar(CP_UTF8, 0, unsafeAddr(str[0]), -1, - addr(buffer), 0) - if count == 0: - raiseOsError(osLastError()) - else: - result = cast[LPWSTR](alloc0((count + 1) * sizeof(OLECHAR))) - let res = MultiByteToWideChar(CP_UTF8, 0, unsafeAddr(str[0]), -1, - result, count) - if res == 0: - raiseOsError(osLastError()) - -proc freeBstring(bstr: LPWSTR) = - dealloc(bstr) - -proc getStatus(scode: ULONG): DownloadStatus = - case scode - of 0: result = statusError - of BINDSTATUS_PROXYDETECTING: result = statusProxyDetecting - of BINDSTATUS_REDIRECTING: result = statusRedirecting - of BINDSTATUS_COOKIE_SENT: result = statusCookieSent - of BINDSTATUS_FINDINGRESOURCE: result = statusResolving - of BINDSTATUS_CONNECTING: result = statusConnecting - of BINDSTATUS_SENDINGREQUEST: result = statusRequesting - of BINDSTATUS_MIMETYPEAVAILABLE: result = statusMimetypeAvailable - of BINDSTATUS_BEGINDOWNLOADDATA: result = statusBeginDownloading - of BINDSTATUS_DOWNLOADINGDATA: result = statusDownloading - of BINDSTATUS_ENDDOWNLOADDATA: result = statusEndDownloading - of BINDSTATUS_CACHEFILENAMEAVAILABLE: result = statusCacheAvailable - else: result = statusUnsupported - -proc addRef(self: PIBindStatusCallback): ULONG {.gcsafe, stdcall.} = - inc(self.objectRefCount) - result = self.objectRefCount - -proc release(self: PIBindStatusCallback): ULONG {.gcsafe, stdcall.} = - dec(self.objectRefCount) - result = self.objectRefCount - -proc queryInterface(self: PIBindStatusCallback, riid: ptr UUID, - pvObject: ptr pointer): HRESULT {.gcsafe,stdcall.} = - pvObject[] = nil - - if riid[] == IID_IUnknown: - pvObject[] = cast[pointer](self) - elif riid[] == IID_IBindStatusCallback: - pvObject[] = cast[pointer](self) - - if not isNil(pvObject[]): - discard addRef(self) - result = S_OK - else: - result = E_NOINTERFACE - -proc onStartBinding(self: PIBindStatusCallback, dwReserved: DWORD, - pib: pointer): HRESULT {.gcsafe, stdcall.} = - result = S_OK - -proc getPriority(self: PIBindStatusCallback, - pnPriority: ptr LONG): HRESULT {.gcsafe, stdcall.} = - result = E_NOTIMPL - -proc onLowResource(self: PIBindStatusCallback, - dwReserved: DWORD): HRESULT {.gcsafe, stdcall.} = - result = S_OK - -proc onStopBinding(self: PIBindStatusCallback, - hresult: HRESULT, szError: LPWSTR): HRESULT - {.gcsafe, stdcall.} = - result = S_OK - -proc getBindInfo(self: PIBindStatusCallback, - grfBINDF: ptr DWORD, pbindinfo: ptr BINDINFO): HRESULT - {.gcsafe, stdcall.} = - var cbSize = pbindinfo.cbSize - zeroMem(cast[pointer](pbindinfo), cbSize) - pbindinfo.cbSize = cbSize - grfBINDF[] = self.binfoFlags - result = S_OK - -proc onDataAvailable(self: PIBindStatusCallback, - grfBSCF: DWORD, dwSize: DWORD, pformatetc: pointer, - pstgmed: pointer): HRESULT {.gcsafe, stdcall.} = - result = S_OK - -proc onObjectAvailable(self: PIBindStatusCallback, - riid: REFIID, punk: pointer): HRESULT - {.gcsafe, stdcall.} = - result = S_OK - -proc onProgress(self: PIBindStatusCallback, - ulProgress: ULONG, ulProgressMax: ULONG, ulStatusCode: ULONG, - szStatusText: LPWSTR): HRESULT {.gcsafe, stdcall.} = - var message: string - if optUseProgressCallback in self.options: - if not isNil(szStatusText): - message = $szStatusText - else: - message = "" - self.progressCallback(getStatus(ulStatusCode), uint(ulProgress), - uint(ulProgressMax), message) - result = S_OK - -proc newBindStatusCallback(): IBindStatusCallback = - result = IBindStatusCallback() - result.vtable = cast[ptr IBindStatusCallbackVTable]( - alloc0(sizeof(IBindStatusCallbackVTable)) - ) - result.vtable.QueryInterface = queryInterface - result.vtable.AddRef = addRef - result.vtable.Release = release - result.vtable.OnStartBinding = onStartBinding - result.vtable.GetPriority = getPriority - result.vtable.OnLowResource = onLowResource - result.vtable.OnStopBinding = onStopBinding - result.vtable.GetBindInfo = getBindInfo - result.vtable.OnDataAvailable = onDataAvailable - result.vtable.OnObjectAvailable = onObjectAvailable - result.vtable.OnProgress = onProgress - result.objectRefCount = 1 - -proc freeBindStatusCallback(v: var IBindStatusCallback) = - dealloc(v.vtable) - -proc downloadToFile*(szUrl: string, szFileName: string, - options: set[DownloadOptions] = {}, - progresscb: DownloadProgressCallback = nil) = - ## Downloads from URL specified in ``szUrl`` to local filesystem path - ## specified in ``szFileName``. - ## - ## szUrl - ## URL to download, international names are supported. - ## szFileName - ## Destination path for downloading resource. - ## options - ## Downloading options. Currently only 2 options supported. - ## progresscb - ## Callback procedure, which will be called throughout the download - ## process, indicating status and progress. - ## - ## Available downloading options: - ## - ## optUseCache - ## Try to use Windows cache when downloading. - ## optIgnoreSecurity - ## Ignore HTTPS security problems, e.g. self-signed HTTPS certificate. - ## - var bszUrl = szUrl.toBstring() - var bszFile = szFileName.toBstring() - var bstatus = newBindStatusCallback() - - bstatus.options = {} - - if optUseCache notin options: - bstatus.options.incl(optUseCache) - let res = DeleteUrlCacheEntry(bszUrl) - if res == 0: - let err = osLastError() - if err.int notin {ERROR_ACCESS_DENIED, ERROR_FILE_NOT_FOUND}: - freeBindStatusCallback(bstatus) - freeBstring(bszUrl) - freeBstring(bszFile) - raiseOsError(err) - - bstatus.binfoFlags = BINDF_GETNEWESTVERSION or BINDF_RESYNCHRONIZE or - BINDF_PRAGMA_NO_CACHE or BINDF_NO_UI or - BINDF_SILENTOPERATION - - if optIgnoreSecurity in options: - bstatus.binfoFlags = bstatus.binfoFlags or BINDF_IGNORESECURITYPROBLEM - - if not isNil(progresscb): - bstatus.options.incl(optUseProgressCallback) - bstatus.progressCallback = progresscb - - let res = URLDownloadToFile(nil, bszUrl, bszFile, 0, addr bstatus) - if FAILED(res): - freeBindStatusCallback(bstatus) - freeBstring(bszUrl) - freeBstring(bszFile) - raiseOsError(OSErrorCode(res)) - - freeBindStatusCallback(bstatus) - freeBstring(bszUrl) - freeBstring(bszFile) - -when isMainModule: - proc progress(status: DownloadStatus, progress: uint, progressMax: uint, - message: string) {.gcsafe.} = - const downset: set[DownloadStatus] = {statusBeginDownloading, - statusDownloading, statusEndDownloading} - if status in downset: - var message = "Downloaded " & $progress & " of " & $progressMax & "\c" - stdout.write(message) - else: - echo "Status [" & $status & "] message = [" & $message & "]" - - downloadToFile("https://nim-lang.org/download/mingw64.7z", - "test.zip", {optUseCache}, progress) diff --git a/tools/website.nimf b/tools/website.nimf deleted file mode 100644 index cea30f74fe61..000000000000 --- a/tools/website.nimf +++ /dev/null @@ -1,266 +0,0 @@ -#? stdtmpl | standard -#proc generateHTMLPage(c: var TConfigData, currentTab, title, content, rss, -# rootDir = ""): string = -# result = "" - - - - - ${title} - $c.projectTitle - - - #if len(rss) > 0: - - #end if - - - - - - -# if currentTab == "index": -
    -# else: -
    -# end -
    -
    - -# if currentTab == "index": -
    - -
    -
    -

    Nim is simple..

    -
    -# compute average line length
    -var
    -  sum = 0
    -  count = 0
    -
    -for line in stdin.lines:
    -  sum += line.len
    -  count += 1
    -
    -echo("Average line length: ",
    -  if count > 0: sum / count else: 0)
    -
    -
    -
    -

    ..and type safe...

    -
    -# create and greet someone
    -type Person = object
    -  name: string
    -  age: int
    -
    -proc greet(p: Person) =
    -  echo "Hi, I'm ", p.name, "."
    -  echo "I am ", p.age, " years old."
    -
    -let p = Person(name:"Jon", age:18)
    -p.greet() # or greet(p)
    -
    -
    -
    -
    -
    -

    C FFI is easy in Nim..

    -
    -# declare a C procedure..
    -proc unsafeScanf(f: File, s: cstring)
    -  {.varargs,
    -    importc: "fscanf",
    -    header: "<stdio.h>".}
    -
    -# ..and use it...
    -var x: cint
    -stdin.unsafeScanf("%d", addr x)
    -
    -

    Compile and run with:
        $ nim c -r example.nim

    -
    -
    -

    ..and DSLs are too...

    -
    -# a simple html server
    -import
    -  jester, asyncdispatch, htmlgen
    -
    -routes:
    -  get "/":
    -    resp h1("Hello world")
    -
    -runForever()
    -
    -

    View in browser at:
        localhost:5000

    -
    -
    -
    - - A printed copy of Nim in Action should be available in March 2017! - -
    -
    - - Meet our BountySource sponsors! - -
    -
    -
    -
    -
    -
    -
    -
    -# end - -
    -
    - -
    -
    -
    -
    -
    - $content -
    -
    -
    - - - - -# if currentTab == "index": - -# end if -# if c.gaId.len != 0: - -# end if - - -#end proc -# -# -#proc generateSponsors(sponsors: seq[Sponsor]): string = -#result = "" -#for sponsor in sponsors: -
    - #if sponsor.url.len > 0: - ${sponsor.name} - #else: - ${sponsor.name} - #end if -
    - -
    - Donated $$${sponsor.thisMonth} this month -
    -
    - Donated $$${sponsor.allTime} in total since ${sponsor.since} -
    -#end for -#end proc -#proc generateSponsorsPage(activeSponsors, inactiveSponsors: seq[Sponsor]): string = -#result = "" -

    Our Current Sponsors

    -

    This section lists the companies and individuals that are, very kindly, contributing a -monthly amount to help sustain Nim's development. For more details take a -look at the Bountysource campaign.

    -

    Last updated: ${getTime().utc().format("dd/MM/yyyy")}

    -
    -${generateSponsors(activeSponsors)} -
    -# -

    Our Past Sponsors

    -

    This section lists the companies and individuals that have contributed -money in the past to help sustain Nim's development. For more details take a -look at the Bountysource campaign.

    -
    -${generateSponsors(inactiveSponsors)} -
    -# -#end proc